diff --git a/@file_array/private/file2mat.c b/@file_array/private/file2mat.c index 744a6069..7b3c6035 100644 --- a/@file_array/private/file2mat.c +++ b/@file_array/private/file2mat.c @@ -1,5 +1,5 @@ /* - * $Id: file2mat.c 5446 2013-04-24 16:56:51Z guillaume $ + * $Id: file2mat.c 6618 2015-12-01 16:25:38Z spm $ * John Ashburner */ @@ -625,7 +625,20 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) if (map.dtype->channels == 1) { plhs[0] = mxCreateNumericArray(ndim,odim,map.dtype->clss,mxREAL); - map.dtype->func(ndim-1, idim, iptr, idat, odim, mxGetData(plhs[0])); +#ifdef SPM_WIN32 + /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa366801.aspx */ + __try + { +#endif + map.dtype->func(ndim-1, idim, iptr, idat, odim, mxGetData(plhs[0])); +#ifdef SPM_WIN32 + } + __except(GetExceptionCode()==EXCEPTION_IN_PAGE_ERROR ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + mexErrMsgTxt("An exception occured while accessing the data."); + } +#endif if (map.swap) map.dtype->swap(ocumprod[ndim],mxGetData(plhs[0])); } diff --git a/@file_array/private/file2mat.mexw64 b/@file_array/private/file2mat.mexw64 index f198a48f..61a0e7be 100755 Binary files a/@file_array/private/file2mat.mexw64 and b/@file_array/private/file2mat.mexw64 differ diff --git a/@file_array/private/init.mexw32 b/@file_array/private/init.mexw32 index 8298208f..92ef6d97 100755 Binary files a/@file_array/private/init.mexw32 and b/@file_array/private/init.mexw32 differ diff --git a/@file_array/private/init.mexw64 b/@file_array/private/init.mexw64 index 11c54206..a7f1b272 100755 Binary files a/@file_array/private/init.mexw64 and b/@file_array/private/init.mexw64 differ diff --git a/@file_array/subsasgn.m b/@file_array/subsasgn.m index 7fb60c94..452f9962 100644 --- a/@file_array/subsasgn.m +++ b/@file_array/subsasgn.m @@ -4,7 +4,7 @@ % Copyright (C) 2005-2013 Wellcome Trust Centre for Neuroimaging % -% $Id: subsasgn.m 6157 2014-09-05 18:17:54Z guillaume $ +% $Id: subsasgn.m 6522 2015-08-14 18:55:42Z john $ if isempty(subs), return; end @@ -145,7 +145,13 @@ %-Convert data into output datatype %-------------------------------------------------------------------------- -if dt(ind).isint, dat = round(dat); end +if dt(ind).isint + % Avoid "Warning: Out of range value converted to intmin() or intmax()." + dat = max(dat,dt(ind).min); + dat = min(dat,dt(ind).max); + + dat = round(dat); +end %ws = warning('off'); % Avoid warning messages in R14 SP3 dat = feval(dt(ind).conv,dat); diff --git a/@gifti/fieldnames.m b/@gifti/fieldnames.m index a86fbcc0..b2cc94e2 100644 --- a/@gifti/fieldnames.m +++ b/@gifti/fieldnames.m @@ -7,10 +7,10 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: fieldnames.m 6345 2015-02-20 12:25:50Z guillaume $ +% $Id: fieldnames.m 6507 2015-07-24 16:48:02Z guillaume $ if numel(this) > 1, warning('Only handle scalar objects yet.'); end -pfn = {'vertices' 'faces' 'normals' 'cdata' 'mat' 'labels' 'indices'}; +pfn = {'vertices','faces','normals','cdata','mat','labels','indices'}; -names = pfn(isintent(this,pfn)); +names = unique(pfn(isintent(this,pfn))); diff --git a/@gifti/gifti.m b/@gifti/gifti.m index 29481f24..b1b8bf0a 100644 --- a/@gifti/gifti.m +++ b/@gifti/gifti.m @@ -8,7 +8,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: gifti.m 6347 2015-02-24 17:59:16Z guillaume $ +% $Id: gifti.m 6601 2015-11-19 13:55:32Z guillaume $ switch nargin @@ -67,6 +67,9 @@ elseif strcmpi(e,'.asc') || strcmpi(e,'.srf') this = read_freesurfer_file(varargin{1}); this = gifti(this); + elseif strcmpi(e,'.vtk') + this = mvtk_read(varargin{1}); + this = gifti(this); else this = read_gifti_file(varargin{1},giftistruct); this = class(this,'gifti'); diff --git a/@gifti/isfield.m b/@gifti/isfield.m index d6fb5b11..275578be 100644 --- a/@gifti/isfield.m +++ b/@gifti/isfield.m @@ -8,6 +8,6 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: isfield.m 2076 2008-09-10 12:34:08Z guillaume $ +% $Id: isfield.m 6507 2015-07-24 16:48:02Z guillaume $ -tf = ismember(field, fieldnames(this)); \ No newline at end of file +tf = ismember(field, fieldnames(this)); diff --git a/@gifti/private/mvtk_read.m b/@gifti/private/mvtk_read.m new file mode 100644 index 00000000..a614e1b4 --- /dev/null +++ b/@gifti/private/mvtk_read.m @@ -0,0 +1,165 @@ +function M = mvtk_read(filename) +% Read VTK formatted data from disk +% FORMAT M = mvtk_read(filename) +% +% filename - VTK-formatted file name +% M - data structure +%__________________________________________________________________________ +% +% VTK File Formats Specifications: +% http://www.vtk.org/VTK/img/file-formats.pdf +% +% Requirements: zstream, base64decode +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: mvtk_read.m 6601 2015-11-19 13:55:32Z guillaume $ + + +[pth,name,ext] = fileparts(filename); +switch ext + case '.vtk' + M = mvtk_read_legacy(filename); + case {'.vti','.vtp','.vtr','.vts','.vtu'} + % Serial vtkImageData (structured) + % Serial vtkPolyData (unstructured) + % Serial vtkRectilinearGrid (structured) + % Serial vtkStructuredGrid (structured) + % Serial vtkUnstructuredGrid (unstructured) + M = mvtk_read_xml(filename); + otherwise + error('Unknown file format.'); +end + +%========================================================================== +% function M = mvtk_read_legacy(filename) +%========================================================================== +function M = mvtk_read_legacy(filename) + +fid = fopen(filename,'rt'); +if fid == -1 + error('Cannot open %s.',filename); +end + +%- Part 1: file version and identifier +% # vtk DataFile Version 2.0 +l = fgetl(fid); +if ~ischar(l), error('VTK file incomplete.'); end +if ~strncmpi(l,'# vtk DataFile Version',22) + error('This is not a VTK formatted file.'); +end + +%- Part 2: header +l = fgetl(fid); +if ~ischar(l), error('VTK file incomplete.'); end + +%- Part 3: file format +format = fgetl(fid); +if ~ismember(format,{'ASCII','BINARY'}) + error('Unknown file format.'); +end + +%- Part 4: dataset structure +data_attributes = false; +l = fgetl(fid); +if ~ischar(l), error('VTK file incomplete.'); end +[D,l] = strtok(l); +if ~strcmp(D,'DATASET'), error('Invalid VTK file.'); end +F = strtok(l(2:end)); +switch F + case 'STRUCTURED_POINTS' + warning('Unsupported dataset format.'); + l = fgetl(fid); + DIM = sscanf(l,'DIMENSIONS %d %d %d'); + l = fgetl(fid); + ORIGIN = sscanf(l,'ORIGIN %f %f %f'); + l = fgetl(fid); + SPACING = sscanf(l,'SPACING %f %f %f'); + case 'STRUCTURED_GRID' + warning('Unsupported dataset format.'); + l = fgetl(fid); + DIM = sscanf(l,'DIMENSIONS %d %d %d'); + l = fgetl(fid); + % assume float and n = prod(DIM) + PTS = textscan(fid,'%f %f %f\n',prod(DIM),'CollectOutput',true); + PTS = PTS{1}; + case 'RECTILINEAR_GRID' + warning('Unsupported dataset format.'); + l = fgetl(fid); + DIM = sscanf(l,'DIMENSIONS %d %d %d'); + l = fgetl(fid); + XCOORDS = textscan(fid,'%f',DIM(1),'CollectOutput',true); + XCOORDS = XCOORDS{1}; + l = fgetl(fid); + YCOORDS = textscan(fid,'%f',DIM(2),'CollectOutput',true); + YCOORDS = YCOORDS{1}; + l = fgetl(fid); + ZCOORDS = textscan(fid,'%f',DIM(3),'CollectOutput',true); + ZCOORDS = ZCOORDS{1}; + case 'POLYDATA' + while true + l = fgetl(fid); + if ~ischar(l), break; end + [D,l] = strtok(l); + switch D + case 'POINTS' + [N,l] = strtok(l(2:end)); % l still contains dataType + N = str2double(N); + M.vertices = textscan(fid,'%f %f %f\n',N,'CollectOutput',true); + M.vertices = M.vertices{1}; + case 'POLYGONS' + [N,l] = strtok(l(2:end)); + N = str2double(N); + S = strtok(l); + S = str2double(S); + if 4*N ~= S, error('Unsupported dataset format.'); end + M.faces = textscan(fid,'3 %d %d %d\n',N,'CollectOutput',true); + M.faces = M.faces{1} + 1; + case {'VERTICES','LINES','TRIANGLE_STRIPS'} + error('Unsupported data type.'); + case {'POINT_DATA','CELL_DATA'} + data_attributes = true; + [N,l] = strtok(l(2:end)); + N = str2double(N); + break; + otherwise + error('Invalid VTK file.'); + end + end + case {'UNSTRUCTURED_GRID','FIELD'} + error('Unsupported data type.'); + otherwise + error('Invalid VTK file.'); +end + +%- Part 5: dataset attributes (POINT_DATA and CELL_DATA) +if data_attributes + %l = fgetl(fid); % {POINT_DATA,CELL_DATA} N + l = fgetl(fid); % SCALARS dataName dataType numComp + [P,l] = strtok(l); + [S,l] = strtok(l(2:end)); + [S,l] = strtok(l(2:end)); + S = strtok(l(2:end)); S = str2double(S); + l = fgetl(fid); % LOOKUP_TABLE default + fmt = repmat('%f ',1,S); + fmt = [fmt(1:end-1) '\n']; + M.cdata = textscan(fid,fmt,N,'CollectOutput',true); + M.cdata = M.cdata{1}; +end + +fclose(fid); + +%========================================================================== +% function M = mvtk_read_xml(filename) +%========================================================================== +function M = mvtk_read_xml(filename) + +try + X = xmltree(filename); +catch + error('Cannot parse file %s.',filename); +end + +warning('Unsupported file format.'); +M = struct([]); diff --git a/@gifti/private/mvtk_write.m b/@gifti/private/mvtk_write.m new file mode 100644 index 00000000..e4e7bf30 --- /dev/null +++ b/@gifti/private/mvtk_write.m @@ -0,0 +1,569 @@ +function mvtk_write(M,filename,format) +% Write geometric data on disk using VTK file format (legacy/XML,ascii/binary) +% FORMAT mvtk_write(M,filename,format) +% +% M - data structure +% filename - output filename [Default: 'untitled'] +% format - VTK file format: legacy, legacy-ascii, legacy-binary, xml, +% xml-ascii, xml-binary [Default: 'legacy-ascii'] +%__________________________________________________________________________ +% +% VTK File Formats Specifications: +% http://www.vtk.org/VTK/img/file-formats.pdf +% +% Requirements: zstream, base64encode +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: mvtk_write.m 6601 2015-11-19 13:55:32Z guillaume $ + + +%-Input parameters +%-------------------------------------------------------------------------- +if nargin < 2 || isempty(filename), filename = 'untitled'; end +if nargin < 3 || isempty(format) + [pth,name,ext] = fileparts(filename); + switch ext + case '' + format = 'legacy-ascii'; % default + ext = '.vtk'; + filename = fullfile(pth,[name ext]); + case '.vtk' + format = 'legacy-ascii'; + case 'vtp' + format = 'xml-ascii'; + case {'.vti','.vtr','.vts','.vtu'} + format = 'xml-ascii'; + warning('Only partially handled.'); + otherwise + error('Unknown file extension.'); + end +else + switch lower(format) + case {'legacy','legacy-ascii','legacy-binary'} + ext = '.vtk'; + case {'xml','xml-ascii','xml-binary','xml-appended'} + ext = '.vtp'; + otherwise + error('Unknown file format.'); + end +end + +%-Filename +%-------------------------------------------------------------------------- +[pth,name,e] = fileparts(filename); +if ~strcmpi(e,ext) + warning('Changing file extension from %s to %s.',e,ext); +end +filename = fullfile(pth,[name ext]); + +%-Convert input structure if necessary +%-------------------------------------------------------------------------- + +%-Three scalars per item interpreted as color +% if isfield(M,'cdata') && size(M.cdata,2) == 3 +% M.color = M.cdata; +% M = rmfield(M,'cdata'); +% end + +%-Compute normals +if ~isfield(M,'normals') + M.normals = compute_normals(M); +end + +%-Write file +%-------------------------------------------------------------------------- +switch lower(format) + case {'legacy','legacy-ascii'} + mvtk_write_legacy(M,filename,'ASCII'); + case {'legacy-binary'} + mvtk_write_legacy(M,filename,'BINARY'); + case {'xml','xml-ascii'} + mvtk_write_xml(M,filename,'ASCII'); + case {'xml-binary'} + mvtk_write_xml(M,filename,'BINARY'); + case {'xml-appended'} + mvtk_write_xml(M,filename,'APPENDED'); + otherwise + error('Unknown file format.'); +end + + +%========================================================================== +% function fid = mvtk_write_legacy(s,filename,format) +%========================================================================== +function fid = mvtk_write_legacy(s,filename,format) + +%-Open file +%-------------------------------------------------------------------------- +if nargin == 2, format = 'ASCII'; else format = upper(format); end +switch format + case 'ASCII' + fopen_opts = {'wt'}; + write_data = @(fid,fmt,prec,dat) fprintf(fid,fmt,dat); + case 'BINARY' + fopen_opts = {'wb','ieee-be'}; + write_data = @(fid,fmt,prec,dat) [fwrite(fid,dat,prec);fprintf(fid,'\n');]; + otherwise + error('Unknown file format.'); +end +fid = fopen(filename,fopen_opts{:}); +if fid == -1 + error('Unable to write file %s: permission denied.',filename); +end + +%-Legacy VTK file format +%========================================================================== + +%- Part 1: file version and identifier +%-------------------------------------------------------------------------- +fprintf(fid,'# vtk DataFile Version 2.0\n'); + +%- Part 2: header +%-------------------------------------------------------------------------- +hdr = 'Saved using mVTK'; +fprintf(fid,'%s\n',hdr(1:min(length(hdr),256))); + +%- Part 3: data type (either ASCII or BINARY) +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',format); + +%- Part 4: dataset structure: geometry/topology +%-------------------------------------------------------------------------- +% One of: STRUCTURED_POINTS, STRUCTURED_GRID, UNSTRUCTURED_GRID, POLYDATA, +% RECTILINEAR_GRID, FIELD +if isfield(s,'vertices') && isfield(s,'faces') + type = 'POLYDATA'; +elseif isfield(s,'vertices') + type = 'UNSTRUCTURED_GRID'; +elseif isfield(s,'spacing') + type = 'STRUCTURED_POINTS'; +%elseif isfield(s,'mat') +% type = 'STRUCTURED_GRID'; +else + error('Unknown dataset structure.'); +end +fprintf(fid,'DATASET %s\n',type); +if isfield(s,'vertices') + fprintf(fid,'POINTS %d %s\n',size(s.vertices,1),'float'); + write_data(fid,'%f %f %f\n','float32',s.vertices'); +end +if isfield(s,'faces') + nFaces = size(s.faces,1); + nConn = size(s.faces,2); + fprintf(fid,'POLYGONS %d %d\n',nFaces,nFaces*(nConn+1)); + dat = uint32([repmat(nConn,1,nFaces); (s.faces'-1)]); + fmt = repmat('%d ',1,size(dat,1)); fmt(end) = ''; + write_data(fid,[fmt '\n'],'uint32',dat); +end +if isfield(s,'spacing') + fprintf(fid,'DIMENSIONS %d %d %d\n',size(s.cdata)); + fprintf(fid,'ORIGIN %f %f %f\n',s.origin); + fprintf(fid,'SPACING %f %f %f\n',s.spacing); + s.cdata = s.cdata(:); +end +% if isfield(s,'mat') +% dim = size(s.cdata); +% fprintf(fid,'DIMENSIONS %d %d %d\n',dim); +% fprintf(fid,'POINTS %d %s\n',prod(dim),'float'); +% [R,C,P] = ndgrid(1:dim(1),1:dim(2),1:dim(3)); +% RCP = [R(:)';C(:)';P(:)']; +% clear R C P +% RCP(4,:) = 1; +% XYZmm = s.mat(1:3,:)*RCP; +% write_data(fid,'%f %f %f\n','float32',XYZmm); +% s.cdata = s.cdata(:); +% end +fprintf(fid,'\n'); + +%- Part 5: dataset attributes (POINT_DATA and CELL_DATA) +%-------------------------------------------------------------------------- +point_data_hdr = false; + +%-SCALARS (and LOOKUP_TABLE) +if isfield(s,'cdata') && ~isempty(s.cdata) + if ~point_data_hdr + fprintf(fid,'POINT_DATA %d\n',size(s.cdata,1)); + point_data_hdr = true; + end + if ~isfield(s,'lut') + lut_name = 'default'; + else + lut_name = 'my_lut'; + if size(s.lut,2) == 3 + s.lut = [s.lut ones(size(s.lut,1),1)]; % alpha + end + end + dataName = 'cdata'; + fprintf(fid,'SCALARS %s %s %d\n',dataName,'float',size(s.cdata,2)); + fprintf(fid,'LOOKUP_TABLE %s\n',lut_name); + fmt = repmat('%f ',1,size(s.cdata,2)); fmt(end) = ''; + write_data(fid,[fmt '\n'],'float32',s.cdata'); + if ~strcmp(lut_name,'default') + fprintf(fid,'LOOKUP_TABLE %s %d\n',lut_name,size(s.lut,1)); + if strcmp(format,'ASCII') + % float values between (0,1) + write_data(fid,'%f %f %f %f\n','float32',s.lut'); % rescale + else + % four unsigned char values per table entry + write_data(fid,'','uint8',uint8(s.lut')); % rescale + end + end +end + +%-COLOR_SCALARS +if isfield(s,'color') && ~isempty(s.color) + if ~point_data_hdr + fprintf(fid,'POINT_DATA %d\n',size(s.color,1)); + point_data_hdr = true; + end + dataName = 'color'; + fprintf(fid,'COLOR_SCALARS %s %d\n',dataName,size(s.color,2)); + if strcmp(format,'ASCII') + % nValues float values between (0.1) + fmt = repmat('%f ',1,size(s.color,2)); fmt(end) = ''; + write_data(fid,[fmt '\n'],'float32',s.color'); % rescale + else + % nValues unsigned char values per scalar value + write_data(fid,'','uint8',uint8(s.color')); % rescale + end +end + +%-VECTORS +if isfield(s,'vectors') && ~isempty(s.vectors) + if ~point_data_hdr + fprintf(fid,'POINT_DATA %d\n',size(s.vectors,1)); + point_data_hdr = true; + end + dataName = 'vectors'; + fprintf(fid,'VECTORS %s %s\n',dataName,'float'); + write_data(fid,'%f %f %f\n','float32',s.vectors'); +end + +%-NORMALS +if isfield(s,'normals') && ~isempty(s.normals) + if ~point_data_hdr + fprintf(fid,'POINT_DATA %d\n',size(s.vertices,1)); + point_data_hdr = true; + end + dataName = 'normals'; + fprintf(fid,'NORMALS %s %s\n',dataName,'float'); + write_data(fid,'%f %f %f\n','float32',-s.normals'); +end + +%-TENSORS +if isfield(s,'tensors') && ~isempty(s.tensors) + if ~point_data_hdr + fprintf(fid,'POINT_DATA %d\n',size(s.tensors,1)); + point_data_hdr = true; + end + dataName = 'tensors'; + fprintf(fid,'TENSORS %s %s\n',dataName,'float'); + write_data(fid,repmat('%f %f %f\n',1,3),'float32',s.tensors'); +end + +%-Close file +%-------------------------------------------------------------------------- +fclose(fid); + + +%========================================================================== +% function fid = mvtk_write_xml(s,filename,format) +%========================================================================== +function fid = mvtk_write_xml(s,filename,format) + +%-Open file +%-------------------------------------------------------------------------- +if nargin == 2, format = 'ascii'; else format = lower(format); end +clear store_appended_data +switch format + case 'ascii' + fopen_opts = {'wt'}; + write_data = @(fmt,dat) deal(NaN,sprintf(fmt,dat)); + case 'binary' + fopen_opts = {'wb','ieee-le'}; + write_data = @(fmt,dat) deal(NaN,[... + base64encode(typecast(uint32(numel(dat)*numel(typecast(dat(1),'uint8'))),'uint8')) ... + base64encode(typecast(dat(:),'uint8'))]); + case 'appended' + fopen_opts = {'wt'}; + store_appended_data('start'); + store_appended_data('base64'); % format: raw, [base64] + store_appended_data('zlib'); % compression: none, [zlib] + write_data = @(fmt,dat) deal(store_appended_data(fmt,dat),''); + otherwise + error('Unknown format.'); +end +fid = fopen(filename,fopen_opts{:}); +if fid == -1 + error('Unable to write file %s: permission denied.',filename); +end + +%-XML VTK file format +%========================================================================== +o = @(x) blanks(x*3); + +%-XML prolog +%-------------------------------------------------------------------------- +fprintf(fid,'\n'); + +%-VTKFile +%-------------------------------------------------------------------------- +VTKFile = struct; +VTKFile.type = 'PolyData'; +VTKFile.version = '0.1'; +VTKFile.byte_order = 'LittleEndian'; +VTKFile.header_type = 'UInt32'; +if strcmp(store_appended_data('compression'),'zlib') + VTKFile.compressor = 'vtkZLibDataCompressor'; +end +fprintf(fid,'\n'); + +%-PolyData +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(1)); +Piece = struct; +Piece.NumberOfPoints = sprintf('%d',size(s.vertices,1)); +Piece.NumberOfVerts = sprintf('%d',0); +Piece.NumberOfLines = sprintf('%d',0); +Piece.NumberOfStrips = sprintf('%d',0); +Piece.NumberOfPolys = sprintf('%d',size(s.faces,1)); +fprintf(fid,'%s\n'); + +%-PointData +%-------------------------------------------------------------------------- +PointData = struct; +if isfield(s,'cdata') && ~isempty(s.cdata) + PointData.Scalars = 'scalars'; +end +if isfield(s,'normals') && ~isempty(s.normals) + PointData.Normals = 'normals'; +end +fprintf(fid,'%s\n'); + +%-Scalars +if isfield(s,'cdata') && ~isempty(s.cdata) + [offset,dat] = write_data('%f ',single(s.cdata')); + DataArray = struct; + DataArray.type = 'Float32'; + DataArray.Name = 'scalars'; + DataArray.NumberOfComponents = sprintf('%d',size(s.cdata,2)); + DataArray.format = format; + if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end + fprintf(fid,'%s%s\n',dat); +end + +%-Normals +if isfield(s,'normals') && ~isempty(s.normals) + [offset,dat] = write_data('%f ',single(-s.normals')); + DataArray = struct; + DataArray.type = 'Float32'; + DataArray.Name = 'normals'; + DataArray.NumberOfComponents = sprintf('%d',3); + DataArray.format = format; + if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end + fprintf(fid,'%s%s\n',dat); +end + +fprintf(fid,'%s\n',o(3)); + +%-CellData +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(3)); + +%-Points +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(3)); +if isfield(s,'vertices') + [offset,dat] = write_data('%f ',single(s.vertices')); + DataArray = struct; + DataArray.type = 'Float32'; + DataArray.Name = 'Vertices'; + DataArray.NumberOfComponents = sprintf('%d',3); + DataArray.format = format; + if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end + fprintf(fid,'%s%s\n',dat); +end +fprintf(fid,'%s\n',o(3)); + +%-Verts +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(3)); + +%-Lines +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(3)); + +%-Strips +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(3)); + +%-Polys +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(3)); +if isfield(s,'faces') + [offset,dat] = write_data('%d ',uint32(s.faces'-1)); + DataArray = struct; + DataArray.type = 'UInt32'; + DataArray.Name = 'connectivity'; + DataArray.format = format; + if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end + fprintf(fid,'%s%s\n',dat); + + [offset,dat] = write_data('%d ',uint32(3:3:3*size(s.faces,1))); + DataArray = struct; + DataArray.type = 'UInt32'; + DataArray.Name = 'offsets'; + DataArray.format = format; + if ~isnan(offset), DataArray.offset = sprintf('%d',offset); end + fprintf(fid,'%s%s\n',dat); +end +fprintf(fid,'%s\n',o(3)); + +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s\n',o(1)); + +%-AppendedData +%-------------------------------------------------------------------------- +if strcmp(format,'appended') + dat = store_appended_data('retrieve'); + store_appended_data('stop'); + AppendedData = struct; + AppendedData.encoding = store_appended_data('encoding'); + fprintf(fid,'%s\n%s_',o(2)); + fwrite(fid,dat); + fprintf(fid,'\n%s\n',o(1)); +end + +fprintf(fid,'\n'); + +%-Close file +%-------------------------------------------------------------------------- +fclose(fid); + + +%========================================================================== +% function varargout = store_appended_data(fmt,dat) +%========================================================================== +function varargout = store_appended_data(fmt,dat) + +persistent fid encoding compression + +if isempty(encoding), encoding = 'raw'; end +if isempty(compression), compression = 'none'; end +if ~nargin, fmt = 'start'; end +if nargin < 2 + varargout = {}; + switch lower(fmt) + case 'start' + filename = tempname; + fid = fopen(filename,'w+b'); + if fid == -1 + error('Cannot open temporary file.'); + end + case 'stop' + filename = fopen(fid); + fclose(fid); + delete(filename); + fid = -1; + case 'retrieve' + frewind(fid); + varargout = {fread(fid)}; + case 'encoding' + varargout = {encoding}; + case 'compression' + varargout = {compression}; + case {'raw','base64'} + encoding = fmt; + case {'none','zlib'} + compression = fmt; + otherwise + error('Unknown action.'); + end + return; +end + +varargout = {ftell(fid)}; +N = uint32(numel(dat)*numel(typecast(dat(1),'uint8'))); +switch encoding + case 'raw' + switch compression + case 'none' + dat = typecast(dat(:),'uint8'); + hdr = N; + case 'zlib' + dat = zstream('C',typecast(dat(:),'uint8')); + hdr = uint32([1 N N numel(dat)]); + otherwise + error('Unknown compression.'); + end + fwrite(fid,hdr,'uint32'); + fwrite(fid,dat,class(dat)); + case 'base64' + switch compression + case 'none' + dat = typecast(dat(:),'uint8'); + hdr = N; + case 'zlib' + dat = zstream('C',typecast(dat(:),'uint8')); + hdr = uint32([1 N N numel(dat)]); + otherwise + error('Unknown compression.'); + end + fwrite(fid,base64encode(typecast(hdr,'uint8'))); + fwrite(fid,base64encode(dat)); + otherwise + error('Unknown encoding.'); +end + + +%========================================================================== +% function N = compute_normals(S) +%========================================================================== +function N = compute_normals(S) +try + t = triangulation(double(S.faces),double(S.vertices)); + N = -double(t.vertexNormal); + normN = sqrt(sum(N.^2,2)); + normN(normN < eps) = 1; + N = N ./ repmat(normN,1,3); +catch + N = []; +end diff --git a/@gifti/private/zstream.mexa64 b/@gifti/private/zstream.mexa64 new file mode 100755 index 00000000..fa41125b Binary files /dev/null and b/@gifti/private/zstream.mexa64 differ diff --git a/@gifti/private/zstream.mexmaci64 b/@gifti/private/zstream.mexmaci64 new file mode 100755 index 00000000..5cb0138d Binary files /dev/null and b/@gifti/private/zstream.mexmaci64 differ diff --git a/@gifti/private/zstream.mexw64 b/@gifti/private/zstream.mexw64 new file mode 100644 index 00000000..969650b6 Binary files /dev/null and b/@gifti/private/zstream.mexw64 differ diff --git a/@gifti/save.m b/@gifti/save.m index b063dd18..a7fd6952 100644 --- a/@gifti/save.m +++ b/@gifti/save.m @@ -1,36 +1,29 @@ function save(this,filename,encoding) % Save GIfTI object in a GIfTI format file -% FORMAT save(this,filename) +% FORMAT save(this,filename,encoding) % this - GIfTI object % filename - name of GIfTI file to be created [Default: 'untitled.gii'] % encoding - optional argument to specify encoding format, among -% ASCII, Base64Binary, GZipBase64Binary, ExternalFileBinary, -% Collada (.dae), IDTF (.idtf). [Default: 'GZipBase64Binary'] +% ASCII, Base64Binary, GZipBase64Binary, ExternalFileBinary. +% [Default: 'GZipBase64Binary'] %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: save.m 6416 2015-04-21 15:34:10Z guillaume $ +% $Id: save.m 6516 2015-08-07 17:28:33Z guillaume $ -% Check filename and file format +% Check filename %-------------------------------------------------------------------------- ext = '.gii'; if nargin == 1 - filename = 'untitled.gii'; -else - if nargin == 3 && strcmpi(encoding,'collada') - ext = '.dae'; - end - if nargin == 3 && strcmpi(encoding,'idtf') - ext = '.idtf'; - end - [p,f,e] = fileparts(filename); - if ~ismember(lower(e),{ext}) - e = ext; - end - filename = fullfile(p,[f e]); + filename = 'untitled'; +end +[p,f,e] = fileparts(filename); +if ~ismember(lower(e),{ext}) + e = ext; end +filename = fullfile(p,[f e]); % Open file for writing %-------------------------------------------------------------------------- @@ -41,22 +34,19 @@ function save(this,filename,encoding) % Write file %-------------------------------------------------------------------------- -switch ext - case '.gii' - if nargin < 3, encoding = 'GZipBase64Binary'; end - fid = save_gii(fid,this,encoding); - case '.dae' - fid = save_dae(fid,this); - case '.idtf' - fid = save_idtf(fid,this); +if nargin < 3, encoding = 'GZipBase64Binary'; end +switch encoding + case {'ASCII','Base64Binary','GZipBase64Binary','ExternalFileBinary'} otherwise - error('Unknown file format.'); + error('Unknown encoding.'); end +fid = save_gii(fid,this,encoding); % Close file %-------------------------------------------------------------------------- fclose(fid); + %========================================================================== % function fid = save_gii(fid,this,encoding) %========================================================================== @@ -84,6 +74,9 @@ function save(this,filename,encoding) for i=1:length(this.data) % Revert the dimension storage d = this.data{i}.attributes.Dim; + if numel(d) > 1 && d(end) == 1 + d = d(1:end-1); + end this.data{i}.attributes = rmfield(this.data{i}.attributes,'Dim'); this.data{i}.attributes.Dimensionality = num2str(length(d)); for j=1:length(d) @@ -258,289 +251,3 @@ function save(this,filename,encoding) end fprintf(fid,'\n'); - -%========================================================================== -% function fid = save_dae(fid,this) -%========================================================================== -function fid = save_dae(fid,this) - -o = @(x) blanks(x*3); - -% Split the mesh into connected components -%-------------------------------------------------------------------------- -s = struct(this); -try - C = spm_mesh_label(s.faces); - d = []; - for i=1:numel(unique(C)) - d(i).faces = s.faces(C==i,:); - u = unique(d(i).faces); - d(i).vertices = s.vertices(u,:); - a = 1:max(d(i).faces(:)); - a(u) = 1:size(d(i).vertices,1); - %a = sparse(1,double(u),1:1:size(d(i).vertices,1)); - d(i).faces = a(d(i).faces); - end - s = d; -end - -% Prolog & root of the Collada XML file -%-------------------------------------------------------------------------- -fprintf(fid,'\n'); -fprintf(fid,'\n'); - -% Assets -%-------------------------------------------------------------------------- -fprintf(fid,'%s\n',o(1)); -fprintf(fid,'%s\n',o(2)); -fprintf(fid,'%s%s\n',o(3),... - 'http://www.fil.ion.ucl.ac.uk/spm/'); -fprintf(fid,'%s%s\n',o(3),'SPM'); -fprintf(fid,'%s\n',o(2)); -fprintf(fid,'%s%s\n',o(2),datestr(now,'yyyy-mm-ddTHH:MM:SSZ')); -fprintf(fid,'%s%s\n',o(2),datestr(now,'yyyy-mm-ddTHH:MM:SSZ')); -fprintf(fid,'%s\n',o(2)); -fprintf(fid,'%sZ_UP\n',o(2)); -fprintf(fid,'%s\n',o(1)); - -% Image, Materials, Effects -%-------------------------------------------------------------------------- -%fprintf(fid,'%s\n',o(1)); - -fprintf(fid,'%s\n',o(1)); -for i=1:numel(s) - fprintf(fid,'%s\n',o(2),i,i); - fprintf(fid,'%s\n',o(3),i); - fprintf(fid,'%s\n',o(2)); -end -fprintf(fid,'%s\n',o(1)); - -fprintf(fid,'%s\n',o(1)); -for i=1:numel(s) - fprintf(fid,'%s\n',o(2),i,i); - fprintf(fid,'%s\n',o(3)); - fprintf(fid,'%s\n',o(4)); - fprintf(fid,'%s\n',o(5)); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s%f %f %f %d\n',o(7),[0 0 0 1]); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s%f %f %f %d\n',o(7),[0 0 0 1]); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s%f %f %f %d\n',o(7),[0.5 0.5 0.5 1]); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s%d %d %d %d\n',o(7),[1 1 1 1]); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s%f\n',o(7),0); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s\n',o(5)); - fprintf(fid,'%s\n',o(4)); - fprintf(fid,'%s\n',o(3)); - fprintf(fid,'%s\n',o(2)); -end -fprintf(fid,'%s\n',o(1)); - -% Geometry -%-------------------------------------------------------------------------- -fprintf(fid,'%s\n',o(1)); -for i=1:numel(s) - fprintf(fid,'%s\n',o(2),i,i); - fprintf(fid,'%s\n',o(3)); - fprintf(fid,'%s\n',o(4),i); - fprintf(fid,'%s',o(5),i,numel(s(i).vertices)); - fprintf(fid,'%f ',repmat(s(i).vertices',1,[])); - fprintf(fid,'\n'); - fprintf(fid,'%s\n',o(5)); - fprintf(fid,'%s\n',o(6),size(s(i).vertices,1),i); - fprintf(fid,'%s\n',o(7)); - fprintf(fid,'%s\n',o(7)); - fprintf(fid,'%s\n',o(7)); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s\n',o(5)); - fprintf(fid,'%s\n',o(4)); - fprintf(fid,'%s\n',o(4),i); - fprintf(fid,'%s\n',o(5),i); - fprintf(fid,'%s\n',o(4)); - fprintf(fid,'%s\n',o(4),i,size(s(i).faces,1)); - fprintf(fid,'%s\n',o(5),i); - fprintf(fid,'%s

',o(5)); - fprintf(fid,'%d ',repmat(s(i).faces',1,[])-1); - fprintf(fid,'

\n'); - fprintf(fid,'%s
\n',o(4)); - fprintf(fid,'%s
\n',o(3)); - fprintf(fid,'%s
\n',o(2)); -end -fprintf(fid,'%s
\n',o(1)); - -% Scene -%-------------------------------------------------------------------------- -fprintf(fid,'%s\n',o(1)); -fprintf(fid,'%s\n',o(2)); -for i=1:numel(s) - fprintf(fid,'%s\n',o(3),i); - fprintf(fid,'%s\n',o(4),i); - fprintf(fid,'%s\n',o(5)); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s\n',o(7),i,i); - fprintf(fid,'%s\n',o(6)); - fprintf(fid,'%s\n',o(5)); - fprintf(fid,'%s\n',o(4)); - fprintf(fid,'%s\n',o(3)); -end -fprintf(fid,'%s\n',o(2)); -fprintf(fid,'%s\n',o(1)); -fprintf(fid,'%s\n',o(1)); -fprintf(fid,'%s\n',o(2)); -fprintf(fid,'%s\n',o(1)); - -% End of XML -%-------------------------------------------------------------------------- -fprintf(fid,'
\n'); - -%========================================================================== -% function fid = save_idtf(fid,this) -%========================================================================== -function fid = save_idtf(fid,this) - -o = @(x) blanks(x*3); - -s = struct(this); - -% Compute normals -%-------------------------------------------------------------------------- -if ~isfield(s,'normals') - try - s.normals = spm_mesh_normals(... - struct('vertices',s.vertices,'faces',s.faces),true); - catch - s.normals = []; - end -end - -% Split the mesh into connected components -%-------------------------------------------------------------------------- -try - C = spm_mesh_label(s.faces); - d = []; - try - if size(s.cdata,2) == 1 && (any(s.cdata>1) || any(s.cdata<0)) - mi = min(s.cdata); ma = max(s.cdata); - s.cdata = (s.cdata-mi)/ (ma-mi); - else - end - end - for i=1:numel(unique(C)) - d(i).faces = s.faces(C==i,:); - u = unique(d(i).faces); - d(i).vertices = s.vertices(u,:); - d(i).normals = s.normals(u,:); - a = 1:max(d(i).faces(:)); - a(u) = 1:size(d(i).vertices,1); - %a = sparse(1,double(u),1:1:size(d(i).vertices,1)); - d(i).faces = a(d(i).faces); - d(i).mat = s.mat; - try - d(i).cdata = s.cdata(u,:); - if size(d(i).cdata,2) == 1 - d(i).cdata = repmat(d(i).cdata,1,3); - end - end - end - s = d; -end - -% FILE_HEADER -%-------------------------------------------------------------------------- -fprintf(fid,'FILE_FORMAT "IDTF"\n'); -fprintf(fid,'FORMAT_VERSION 100\n\n'); - -% NODES -%-------------------------------------------------------------------------- -for i=1:numel(s) - fprintf(fid,'NODE "MODEL" {\n'); - fprintf(fid,'%sNODE_NAME "%s"\n',o(1),sprintf('Mesh%04d',i)); - fprintf(fid,'%sPARENT_LIST {\n',o(1)); - fprintf(fid,'%sPARENT_COUNT %d\n',o(2),1); - fprintf(fid,'%sPARENT %d {\n',o(2),0); - fprintf(fid,'%sPARENT_NAME "%s"\n',o(3),''); - fprintf(fid,'%sPARENT_TM {\n',o(3)); - I = s(i).mat; % eye(4); - for j=1:size(I,2) - fprintf(fid,'%s',o(4)); fprintf(fid,'%f ',I(:,j)'); fprintf(fid,'\n'); - end - fprintf(fid,'%s}\n',o(3)); - fprintf(fid,'%s}\n',o(2)); - fprintf(fid,'%s}\n',o(1)); - fprintf(fid,'%sRESOURCE_NAME "%s"\n',o(1),sprintf('Mesh%04d',i)); - %fprintf(fid,'%sMODEL_VISIBILITY "BOTH"\n',o(1)); - fprintf(fid,'}\n\n'); -end - -% NODE_RESOURCES -%-------------------------------------------------------------------------- -for i=1:numel(s) - fprintf(fid,'RESOURCE_LIST "MODEL" {\n'); - fprintf(fid,'%sRESOURCE_COUNT %d\n',o(1),1); - fprintf(fid,'%sRESOURCE %d {\n',o(1),0); - fprintf(fid,'%sRESOURCE_NAME "%s"\n',o(2),sprintf('Mesh%04d',i)); - fprintf(fid,'%sMODEL_TYPE "MESH"\n',o(2)); - fprintf(fid,'%sMESH {\n',o(2)); - fprintf(fid,'%sFACE_COUNT %d\n',o(3),size(s(i).faces,1)); - fprintf(fid,'%sMODEL_POSITION_COUNT %d\n',o(3),size(s(i).vertices,1)); - fprintf(fid,'%sMODEL_NORMAL_COUNT %d\n',o(3),size(s(i).normals,1)); - if ~isfield(s(i),'cdata') || isempty(s(i).cdata) - c = 0; - else - c = size(s(i).cdata,1); - end - fprintf(fid,'%sMODEL_DIFFUSE_COLOR_COUNT %d\n',o(3),c); - fprintf(fid,'%sMODEL_SPECULAR_COLOR_COUNT %d\n',o(3),0); - fprintf(fid,'%sMODEL_TEXTURE_COORD_COUNT %d\n',o(3),0); - fprintf(fid,'%sMODEL_BONE_COUNT %d\n',o(3),0); - fprintf(fid,'%sMODEL_SHADING_COUNT %d\n',o(3),1); - fprintf(fid,'%sMODEL_SHADING_DESCRIPTION_LIST {\n',o(3)); - fprintf(fid,'%sSHADING_DESCRIPTION %d {\n',o(4),0); - fprintf(fid,'%sTEXTURE_LAYER_COUNT %d\n',o(5),0); - fprintf(fid,'%sSHADER_ID %d\n',o(5),0); - fprintf(fid,'%s}\n',o(4)); - fprintf(fid,'%s}\n',o(3)); - - fprintf(fid,'%sMESH_FACE_POSITION_LIST {\n',o(3)); - fprintf(fid,'%d %d %d\n',s(i).faces'-1); - fprintf(fid,'%s}\n',o(3)); - - fprintf(fid,'%sMESH_FACE_NORMAL_LIST {\n',o(3)); - fprintf(fid,'%d %d %d\n',s(i).faces'-1); - fprintf(fid,'%s}\n',o(3)); - - fprintf(fid,'%sMESH_FACE_SHADING_LIST {\n',o(3)); - fprintf(fid,'%d\n',zeros(size(s(i).faces,1),1)); - fprintf(fid,'%s}\n',o(3)); - - if c - fprintf(fid,'%sMESH_FACE_DIFFUSE_COLOR_LIST {\n',o(3)); - fprintf(fid,'%d %d %d\n',s(i).faces'-1); - fprintf(fid,'%s}\n',o(3)); - end - - fprintf(fid,'%sMODEL_POSITION_LIST {\n',o(3)); - fprintf(fid,'%f %f %f\n',s(i).vertices'); - fprintf(fid,'%s}\n',o(3)); - - fprintf(fid,'%sMODEL_NORMAL_LIST {\n',o(3)); - fprintf(fid,'%f %f %f\n',s(i).normals'); - fprintf(fid,'%s}\n',o(3)); - - if c - fprintf(fid,'%sMODEL_DIFFUSE_COLOR_LIST {\n',o(3)); - fprintf(fid,'%f %f %f\n',s(i).cdata'); - fprintf(fid,'%s}\n',o(3)); - end - - fprintf(fid,'%s}\n',o(2)); - fprintf(fid,'%s}\n',o(1)); - fprintf(fid,'}\n'); -end diff --git a/@gifti/saveas.m b/@gifti/saveas.m new file mode 100644 index 00000000..aed49ddf --- /dev/null +++ b/@gifti/saveas.m @@ -0,0 +1,365 @@ +function saveas(this,filename,format) +% Save GIfTI object in external file format +% FORMAT saveas(this,filename,format) +% this - GIfTI object +% filename - name of file to be created [Default: 'untitled.vtk'] +% format - optional argument to specify encoding format, among +% VTK (.vtk,.vtp), Collada (.dae), IDTF (.idtf). [Default: VTK] +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: saveas.m 6618 2015-12-01 16:25:38Z spm $ + + +% Check filename and file format +%-------------------------------------------------------------------------- +ext = '.vtk'; +if nargin == 1 + filename = ['untitled' ext]; +else + if nargin == 3 && strcmpi(format,'collada') + ext = '.dae'; + end + if nargin == 3 && strcmpi(format,'idtf') + ext = '.idtf'; + end + if nargin == 3 && strncmpi(format,'vtk',3) + format = lower(format(5:end)); + ext = '.vtk'; + end + [p,f,e] = fileparts(filename); + if strcmpi(e,'.gii') + warning('Use save instead of saveas.'); + save(this,filename); + return; + end + if ~ismember(lower(e),{ext}) + warning('Changing file extension from %s to %s.',e,ext); + e = ext; + end + filename = fullfile(p,[f e]); +end + +% Write file +%-------------------------------------------------------------------------- +s = struct(this); + +switch ext + case '.dae' + save_dae(s,filename); + case '.idtf' + save_idtf(s,filename); + case {'.vtk','.vtp'} + if nargin < 3, format = 'legacy-ascii'; end + mvtk_write(s,filename,format); + otherwise + error('Unknown file format.'); +end + + +%========================================================================== +% function save_dae(s,filename) +%========================================================================== +function save_dae(s,filename) + +o = @(x) blanks(x*3); + +% Split the mesh into connected components +%-------------------------------------------------------------------------- +try + C = spm_mesh_label(s.faces); + d = []; + for i=1:numel(unique(C)) + d(i).faces = s.faces(C==i,:); + u = unique(d(i).faces); + d(i).vertices = s.vertices(u,:); + a = 1:max(d(i).faces(:)); + a(u) = 1:size(d(i).vertices,1); + %a = sparse(1,double(u),1:1:size(d(i).vertices,1)); + d(i).faces = a(d(i).faces); + end + s = d; +end + +% Open file for writing +%-------------------------------------------------------------------------- +fid = fopen(filename,'wt'); +if fid == -1 + error('Unable to write file %s: permission denied.',filename); +end + +% Prolog & root of the Collada XML file +%-------------------------------------------------------------------------- +fprintf(fid,'\n'); +fprintf(fid,'\n'); + +% Assets +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(1)); +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s%s\n',o(3),... + 'http://www.fil.ion.ucl.ac.uk/spm/'); +fprintf(fid,'%s%s\n',o(3),'SPM'); +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s%s\n',o(2),datestr(now,'yyyy-mm-ddTHH:MM:SSZ')); +fprintf(fid,'%s%s\n',o(2),datestr(now,'yyyy-mm-ddTHH:MM:SSZ')); +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%sZ_UP\n',o(2)); +fprintf(fid,'%s\n',o(1)); + +% Image, Materials, Effects +%-------------------------------------------------------------------------- +%fprintf(fid,'%s\n',o(1)); + +fprintf(fid,'%s\n',o(1)); +for i=1:numel(s) + fprintf(fid,'%s\n',o(2),i,i); + fprintf(fid,'%s\n',o(3),i); + fprintf(fid,'%s\n',o(2)); +end +fprintf(fid,'%s\n',o(1)); + +fprintf(fid,'%s\n',o(1)); +for i=1:numel(s) + fprintf(fid,'%s\n',o(2),i,i); + fprintf(fid,'%s\n',o(3)); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%f %f %f %d\n',o(7),[0 0 0 1]); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%f %f %f %d\n',o(7),[0 0 0 1]); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%f %f %f %d\n',o(7),[0.5 0.5 0.5 1]); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%d %d %d %d\n',o(7),[1 1 1 1]); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s%f\n',o(7),0); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(3)); + fprintf(fid,'%s\n',o(2)); +end +fprintf(fid,'%s\n',o(1)); + +% Geometry +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(1)); +for i=1:numel(s) + fprintf(fid,'%s\n',o(2),i,i); + fprintf(fid,'%s\n',o(3)); + fprintf(fid,'%s\n',o(4),i); + fprintf(fid,'%s',o(5),i,numel(s(i).vertices)); + fprintf(fid,'%f ',reshape(s(i).vertices',1,[])); + fprintf(fid,'\n'); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(6),size(s(i).vertices,1),i); + fprintf(fid,'%s\n',o(7)); + fprintf(fid,'%s\n',o(7)); + fprintf(fid,'%s\n',o(7)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(4),i); + fprintf(fid,'%s\n',o(5),i); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(4),i,size(s(i).faces,1)); + fprintf(fid,'%s\n',o(5),i); + fprintf(fid,'%s

',o(5)); + fprintf(fid,'%d ',reshape(s(i).faces',1,[])-1); + fprintf(fid,'

\n'); + fprintf(fid,'%s
\n',o(4)); + fprintf(fid,'%s
\n',o(3)); + fprintf(fid,'%s
\n',o(2)); +end +fprintf(fid,'%s
\n',o(1)); + +% Scene +%-------------------------------------------------------------------------- +fprintf(fid,'%s\n',o(1)); +fprintf(fid,'%s\n',o(2)); +for i=1:numel(s) + fprintf(fid,'%s\n',o(3),i); + fprintf(fid,'%s\n',o(4),i); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(7),i,i); + fprintf(fid,'%s\n',o(6)); + fprintf(fid,'%s\n',o(5)); + fprintf(fid,'%s\n',o(4)); + fprintf(fid,'%s\n',o(3)); +end +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s\n',o(1)); +fprintf(fid,'%s\n',o(1)); +fprintf(fid,'%s\n',o(2)); +fprintf(fid,'%s\n',o(1)); + +% End of XML +%-------------------------------------------------------------------------- +fprintf(fid,'
\n'); + +% Close file +%-------------------------------------------------------------------------- +fclose(fid); + + +%========================================================================== +% function save_idtf(s,filename) +%========================================================================== +function save_idtf(s,filename) + +o = @(x) blanks(x*3); + +% Compute normals +%-------------------------------------------------------------------------- +if ~isfield(s,'normals') + try + s.normals = spm_mesh_normals(... + struct('vertices',s.vertices,'faces',s.faces),true); + catch + s.normals = []; + end +end + +% Split the mesh into connected components +%-------------------------------------------------------------------------- +try + C = spm_mesh_label(s.faces); + d = []; + try + if size(s.cdata,2) == 1 && (any(s.cdata>1) || any(s.cdata<0)) + mi = min(s.cdata); ma = max(s.cdata); + s.cdata = (s.cdata-mi)/ (ma-mi); + else + end + end + for i=1:numel(unique(C)) + d(i).faces = s.faces(C==i,:); + u = unique(d(i).faces); + d(i).vertices = s.vertices(u,:); + d(i).normals = s.normals(u,:); + a = 1:max(d(i).faces(:)); + a(u) = 1:size(d(i).vertices,1); + %a = sparse(1,double(u),1:1:size(d(i).vertices,1)); + d(i).faces = a(d(i).faces); + d(i).mat = s.mat; + try + d(i).cdata = s.cdata(u,:); + if size(d(i).cdata,2) == 1 + d(i).cdata = repmat(d(i).cdata,1,3); + end + end + end + s = d; +end + +% Open file for writing +%-------------------------------------------------------------------------- +fid = fopen(filename,'wt'); +if fid == -1 + error('Unable to write file %s: permission denied.',filename); +end + +% FILE_HEADER +%-------------------------------------------------------------------------- +fprintf(fid,'FILE_FORMAT "IDTF"\n'); +fprintf(fid,'FORMAT_VERSION 100\n\n'); + +% NODES +%-------------------------------------------------------------------------- +for i=1:numel(s) + fprintf(fid,'NODE "MODEL" {\n'); + fprintf(fid,'%sNODE_NAME "%s"\n',o(1),sprintf('Mesh%04d',i)); + fprintf(fid,'%sPARENT_LIST {\n',o(1)); + fprintf(fid,'%sPARENT_COUNT %d\n',o(2),1); + fprintf(fid,'%sPARENT %d {\n',o(2),0); + fprintf(fid,'%sPARENT_NAME "%s"\n',o(3),''); + fprintf(fid,'%sPARENT_TM {\n',o(3)); + I = s(i).mat; % eye(4); + for j=1:size(I,2) + fprintf(fid,'%s',o(4)); fprintf(fid,'%f ',I(:,j)'); fprintf(fid,'\n'); + end + fprintf(fid,'%s}\n',o(3)); + fprintf(fid,'%s}\n',o(2)); + fprintf(fid,'%s}\n',o(1)); + fprintf(fid,'%sRESOURCE_NAME "%s"\n',o(1),sprintf('Mesh%04d',i)); + %fprintf(fid,'%sMODEL_VISIBILITY "BOTH"\n',o(1)); + fprintf(fid,'}\n\n'); +end + +% NODE_RESOURCES +%-------------------------------------------------------------------------- +for i=1:numel(s) + fprintf(fid,'RESOURCE_LIST "MODEL" {\n'); + fprintf(fid,'%sRESOURCE_COUNT %d\n',o(1),1); + fprintf(fid,'%sRESOURCE %d {\n',o(1),0); + fprintf(fid,'%sRESOURCE_NAME "%s"\n',o(2),sprintf('Mesh%04d',i)); + fprintf(fid,'%sMODEL_TYPE "MESH"\n',o(2)); + fprintf(fid,'%sMESH {\n',o(2)); + fprintf(fid,'%sFACE_COUNT %d\n',o(3),size(s(i).faces,1)); + fprintf(fid,'%sMODEL_POSITION_COUNT %d\n',o(3),size(s(i).vertices,1)); + fprintf(fid,'%sMODEL_NORMAL_COUNT %d\n',o(3),size(s(i).normals,1)); + if ~isfield(s(i),'cdata') || isempty(s(i).cdata) + c = 0; + else + c = size(s(i).cdata,1); + end + fprintf(fid,'%sMODEL_DIFFUSE_COLOR_COUNT %d\n',o(3),c); + fprintf(fid,'%sMODEL_SPECULAR_COLOR_COUNT %d\n',o(3),0); + fprintf(fid,'%sMODEL_TEXTURE_COORD_COUNT %d\n',o(3),0); + fprintf(fid,'%sMODEL_BONE_COUNT %d\n',o(3),0); + fprintf(fid,'%sMODEL_SHADING_COUNT %d\n',o(3),1); + fprintf(fid,'%sMODEL_SHADING_DESCRIPTION_LIST {\n',o(3)); + fprintf(fid,'%sSHADING_DESCRIPTION %d {\n',o(4),0); + fprintf(fid,'%sTEXTURE_LAYER_COUNT %d\n',o(5),0); + fprintf(fid,'%sSHADER_ID %d\n',o(5),0); + fprintf(fid,'%s}\n',o(4)); + fprintf(fid,'%s}\n',o(3)); + + fprintf(fid,'%sMESH_FACE_POSITION_LIST {\n',o(3)); + fprintf(fid,'%d %d %d\n',s(i).faces'-1); + fprintf(fid,'%s}\n',o(3)); + + fprintf(fid,'%sMESH_FACE_NORMAL_LIST {\n',o(3)); + fprintf(fid,'%d %d %d\n',s(i).faces'-1); + fprintf(fid,'%s}\n',o(3)); + + fprintf(fid,'%sMESH_FACE_SHADING_LIST {\n',o(3)); + fprintf(fid,'%d\n',zeros(size(s(i).faces,1),1)); + fprintf(fid,'%s}\n',o(3)); + + if c + fprintf(fid,'%sMESH_FACE_DIFFUSE_COLOR_LIST {\n',o(3)); + fprintf(fid,'%d %d %d\n',s(i).faces'-1); + fprintf(fid,'%s}\n',o(3)); + end + + fprintf(fid,'%sMODEL_POSITION_LIST {\n',o(3)); + fprintf(fid,'%f %f %f\n',s(i).vertices'); + fprintf(fid,'%s}\n',o(3)); + + fprintf(fid,'%sMODEL_NORMAL_LIST {\n',o(3)); + fprintf(fid,'%f %f %f\n',s(i).normals'); + fprintf(fid,'%s}\n',o(3)); + + if c + fprintf(fid,'%sMODEL_DIFFUSE_COLOR_LIST {\n',o(3)); + fprintf(fid,'%f %f %f\n',s(i).cdata'); + fprintf(fid,'%s}\n',o(3)); + end + + fprintf(fid,'%s}\n',o(2)); + fprintf(fid,'%s}\n',o(1)); + fprintf(fid,'}\n'); +end + +% Close file +%-------------------------------------------------------------------------- +fclose(fid); diff --git a/@gifti/struct.m b/@gifti/struct.m index 3272e368..98f97c93 100644 --- a/@gifti/struct.m +++ b/@gifti/struct.m @@ -7,13 +7,12 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: struct.m 4716 2012-04-19 11:01:32Z guillaume $ +% $Id: struct.m 6507 2015-07-24 16:48:02Z guillaume $ names = fieldnames(this); -names = unique(names); values = cell(length(names), length(this(:))); for i=1:length(names) [values{i,:}] = subsref(this(:), substruct('.',names{i})); end -s = reshape(cell2struct(values,names,1),size(this)); \ No newline at end of file +s = reshape(cell2struct(values,names,1),size(this)); diff --git a/@gifti/subsasgn.m b/@gifti/subsasgn.m index 097f5c74..3b3f7695 100644 --- a/@gifti/subsasgn.m +++ b/@gifti/subsasgn.m @@ -4,108 +4,134 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: subsasgn.m 6416 2015-04-21 15:34:10Z guillaume $ +% $Id: subsasgn.m 6513 2015-08-05 17:52:13Z guillaume $ switch subs(1).type case '.' - if ~ismember(subs(1).subs, {'vertices' 'faces' 'normals' 'cdata','mat','indices','private'}) + if ~ismember(subs(1).subs, ... + {'vertices' 'faces' 'normals' 'cdata','mat','indices','private'}) error('Reference to non-existent field ''%s''.',subs(1).subs); - else - % TODO % handle cases when length(subs) > 1 - [i,n] = isintent(this,subs(1).subs); - if isempty(i) && ~strcmp(subs(1).subs,'private') - n = length(this.data) + 1; - if n==1, this.data = {};end - % TODO % Initialise data field appropriately - this.data{n}.metadata = struct([]); - this.data{n}.space = []; - this.data{n}.attributes.Dim = size(A); - % TODO % set DataType according to intent type - this.data{n}.data = []; - switch subs(1).subs - case {'vertices','mat'} - in = 'NIFTI_INTENT_POINTSET'; - dt = 'NIFTI_TYPE_FLOAT32'; - this.data{n}.space.DataSpace = 'NIFTI_XFORM_UNKNOWN'; - this.data{n}.space.TransformedSpace = 'NIFTI_XFORM_UNKNOWN'; - this.data{n}.space.MatrixData = eye(4); - case 'faces' - in = 'NIFTI_INTENT_TRIANGLE'; - dt = 'NIFTI_TYPE_INT32'; - case 'indices' - in = 'NIFTI_INTENT_NODE_INDEX'; - dt = 'NIFTI_TYPE_INT32'; - case 'normals' - in = 'NIFTI_INTENT_VECTOR'; - dt = 'NIFTI_TYPE_FLOAT32'; - case 'cdata' - in = 'NIFTI_INTENT_NONE'; - dt = 'NIFTI_TYPE_FLOAT32'; - otherwise - error('This should not happen.'); - end - this.data{n}.attributes.Intent = in; - this.data{n}.attributes.DataType = dt; + end + % TODO % handle cases when length(subs) > 1 + [i,n] = isintent(this,subs(1).subs); + if isempty(i) && ~strcmp(subs(1).subs,'private') + n = length(this.data) + 1; + if n==1, this.data = {}; end + % TODO % Initialise data field appropriately + this.data{n}.metadata = struct([]); + this.data{n}.space = []; + this.data{n}.attributes.Dim = size(A); + % TODO % set DataType according to intent type + this.data{n}.data = []; + switch subs(1).subs + case {'vertices','mat'} + in = 'NIFTI_INTENT_POINTSET'; + dt = 'NIFTI_TYPE_FLOAT32'; + this.data{n}.space.DataSpace = 'NIFTI_XFORM_UNKNOWN'; + this.data{n}.space.TransformedSpace = 'NIFTI_XFORM_UNKNOWN'; + this.data{n}.space.MatrixData = eye(4); + case 'faces' + in = 'NIFTI_INTENT_TRIANGLE'; + dt = 'NIFTI_TYPE_INT32'; + case 'indices' + in = 'NIFTI_INTENT_NODE_INDEX'; + dt = 'NIFTI_TYPE_INT32'; + case 'normals' + in = 'NIFTI_INTENT_VECTOR'; + dt = 'NIFTI_TYPE_FLOAT32'; + case 'cdata' + in = 'NIFTI_INTENT_NONE'; + dt = 'NIFTI_TYPE_FLOAT32'; + otherwise + error('This should not happen.'); end - if strcmp(subs(1).subs,'mat') + this.data{n}.attributes.Intent = in; + this.data{n}.attributes.DataType = dt; + end + + switch subs(1).subs + %- .private + %-------------------------------------------------------------- + case 'private' + this = builtin('subsasgn',this,subs(2:end),A); + + % .mat + %-------------------------------------------------------------- + case 'mat' if length(subs) > 1 this.data{n}.space(1).MatrixData = builtin('subsasgn',... this.data{n}.space(1).MatrixData,subs(2:end),A); else - if ndims(A) ~= 2 || any(size(A) ~= [4 4]) + if ~isequal(size(A),[4 4]) error('Invalid Coordinate System Transform Matrix.'); - else - this.data{n}.space(1).MatrixData = A; end + this.data{n}.space(1).MatrixData = A; end - elseif strcmp(subs(1).subs,'private') - this = builtin('subsasgn',this,subs(2:end),A); - else - if strcmp(subs(1).subs,'faces') || strcmp(subs(1).subs,'indices') - if length(subs) > 1 - this.data{n}.data = int32(builtin('subsasgn',this.data{n}.data,subs(2:end),A-1)); - else - this.data{n}.data = int32(A - 1); - this.data{n}.attributes.Dim = size(A); - end + + %- .faces + %-------------------------------------------------------------- + case 'faces' + if length(subs) > 1 + this.data{n}.data = int32(builtin('subsasgn',this.data{n}.data,subs(2:end),A-1)); else - if length(subs) > 1 - if numel(n) == 1 - this.data{n}.data = single(builtin('subsasgn',this.data{n}.data,subs(2:end),A)); - this.data{n}.attributes.Dim = size(this.data{n}.data); - else - if numel(subs(2).subs) == 1 - error('Linear indexing not supported: use multiple subscripts.'); - end - idx = subs(2).subs{2}; - if isequal(idx,':'), idx = 1:numel(this.data); end - for k=1:numel(idx) - s = subs(2); - s.subs{2} = 1; - if numel(A) == 1 - this.data{idx(k)}.data = single(builtin('subsasgn',this.data{idx(k)}.data,s,A)); - else - this.data{idx(k)}.data = single(builtin('subsasgn',this.data{idx(k)}.data,s,A(:,k))); - end - this.data{idx(k)}.attributes.Dim = size(this.data{idx(k)}.data); - end - end + this.data{n}.data = int32(A - 1); + this.data{n}.attributes.Dim = size(A); + end + + %- .indices + %-------------------------------------------------------------- + case 'indices' + if n ~= 1 + this.data = this.data([n setdiff(1:numel(this.data),n)]); + n = 1; + end + if length(subs) > 1 + this.data{n}.data = int32(builtin('subsasgn',this.data{n}.data,subs(2:end),A-1)); + else + A = A(:); + this.data{n}.data = int32(A - 1); + this.data{n}.attributes.Dim = size(A); + end + + %- .vertices, .normals, .cdata + %-------------------------------------------------------------- + otherwise + if length(subs) > 1 + if numel(n) == 1 + this.data{n}.data = single(builtin('subsasgn',this.data{n}.data,subs(2:end),A)); + this.data{n}.attributes.Dim = size(this.data{n}.data); else - if numel(n) == 1 - if isa(A,'file_array') - this.data{n}.data = A; - this.data{n}.attributes.Dim = A.dim; + if numel(subs(2).subs) == 1 + error('Linear indexing not supported: use multiple subscripts.'); + end + idx = subs(2).subs{2}; + if isequal(idx,':'), idx = 1:numel(this.data); end + for k=1:numel(idx) + s = subs(2); + s.subs{2} = 1; + if numel(A) == 1 + this.data{idx(k)}.data = single(builtin('subsasgn',this.data{idx(k)}.data,s,A)); else - this.data{n}.data = single(A); - this.data{n}.attributes.Dim = size(A); + this.data{idx(k)}.data = single(builtin('subsasgn',this.data{idx(k)}.data,s,A(:,k))); end + this.data{idx(k)}.attributes.Dim = size(this.data{idx(k)}.data); + end + end + else + if numel(n) == 1 + if isa(A,'file_array') + this.data{n}.data = A; + this.data{n}.attributes.Dim = A.dim; else - error('Syntax not implemented.'); + this.data{n}.data = single(A); + this.data{n}.attributes.Dim = size(A); end + else + error('Syntax not implemented.'); end end - end end + case '()' case '{}' otherwise diff --git a/@meeg/check.m b/@meeg/check.m index 8684bfa6..96d72b66 100644 --- a/@meeg/check.m +++ b/@meeg/check.m @@ -13,7 +13,7 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: check.m 5933 2014-03-28 13:22:28Z vladimir $ +% $Id: check.m 6542 2015-09-09 11:48:34Z karl $ if nargin == 1 option = 'basic'; @@ -147,7 +147,7 @@ if isequal(option, 'dcm') if strcmp(option, 'dcm') - if ~ismember(modality(this, 0), {'EEG', 'MEG', 'MEGPLANAR', 'Multimodal', 'LFP'}) + if ~ismember(modality(this, 0), {'EEG', 'MEG', 'MEGPLANAR', 'Multimodal', 'LFP','ILAM'}) ok = 0; disp('Unsupported modality for DCM'); end diff --git a/@meeg/fiducials.m b/@meeg/fiducials.m index f4466b42..322c971a 100644 --- a/@meeg/fiducials.m +++ b/@meeg/fiducials.m @@ -5,12 +5,12 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: fiducials.m 5025 2012-10-31 14:44:13Z vladimir $ +% $Id: fiducials.m 6622 2015-12-03 11:54:13Z vladimir $ switch nargin case 1 res = this.fiducials; case 2 - this.fiducials = newfiducials; + this.fiducials = ft_struct2double(fixpnt(newfiducials)); res = this; end diff --git a/@meeg/meeg.m b/@meeg/meeg.m index 868a7cc5..225ac873 100644 --- a/@meeg/meeg.m +++ b/@meeg/meeg.m @@ -46,6 +46,10 @@ % .bad - 0 or 1 flag to allow rejection of trials. % .repl - for epochs that are averages - number of replications used % for the average. +% .tag - the user can put any data here that will be attached to +% the respective trial. This is useful e.g. to make sure the +% relation between regressors and data is not broken when +% removing bad trials or merging files. % .events - this is a structure array describing events related to % each trial. % @@ -114,7 +118,7 @@ % Copyright (C) 2005-2011 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: meeg.m 5343 2013-03-21 16:07:50Z vladimir $ +% $Id: meeg.m 6525 2015-08-20 10:03:16Z vladimir $ switch nargin case 0 diff --git a/@meeg/modality.m b/@meeg/modality.m index 989c2851..99e3fd29 100644 --- a/@meeg/modality.m +++ b/@meeg/modality.m @@ -12,7 +12,7 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: modality.m 5212 2013-01-26 13:16:36Z vladimir $ +% $Id: modality.m 6542 2015-09-09 11:48:34Z karl $ if nargin == 1 scalp = 1; @@ -47,6 +47,10 @@ list = [list {'LFP'}]; end +if ~isempty(indchantype(this, 'ILAM')) && ~scalp + list = [list {'ILAM'}]; +end + switch numel(list) case 0 res = 'Other'; diff --git a/@meeg/private/checkmeeg.m b/@meeg/private/checkmeeg.m index 05ba14f5..2f0540b1 100644 --- a/@meeg/private/checkmeeg.m +++ b/@meeg/private/checkmeeg.m @@ -6,7 +6,7 @@ % Copyright (C) 2008-2014 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: checkmeeg.m 6302 2015-01-13 16:11:27Z vladimir $ +% $Id: checkmeeg.m 6622 2015-12-03 11:54:13Z vladimir $ %-Initialise data dimensions @@ -219,6 +219,11 @@ if ~isfield(this.trials, 'bad') [this.trials.bad] = deal(0); end + + if ~isfield(this.trials, 'tag') + [this.trials.tag] = deal([]); + end + if ~isfield(this.trials, 'events') [this.trials.events] = deal([]); end @@ -378,7 +383,7 @@ if ~isfield(this, 'fiducials') this.fiducials = struct([]); else - this.fiducials = ft_struct2double(this.fiducials); + this.fiducials = ft_struct2double(fixpnt(this.fiducials)); end if ~isfield(this, 'artifacts') diff --git a/@meeg/private/fixpnt.m b/@meeg/private/fixpnt.m new file mode 100644 index 00000000..d91eabaa --- /dev/null +++ b/@meeg/private/fixpnt.m @@ -0,0 +1,42 @@ +function data = fixpnt(data, recurse) + +% helper function to replace pos by pnt + +% $Id: fixpnt.m 6682 2016-01-15 15:52:00Z vladimir $ + +if nargin==1 + recurse = 1; +end + +if ~isa(data, 'struct') + return; +end + +if numel(data)>1 + % loop over all individual elements + clear tmp + for i=1:numel(data) + % this is to prevent an "Subscripted assignment between dissimilar structures" error + tmp(i) = fixpnt(data(i)); + end + data = tmp; + clear tmp + return +end + +% replace pos by pnt +if isfield(data, 'pos') + data.pnt = data.pos; + data = rmfield(data, 'pos'); +end + +if recurse<3 + % recurse into substructures, not too deep + fn = fieldnames(data); + fn = setdiff(fn, {'cfg'}); % don't recurse into the cfg structure + for i=1:length(fn) + if isstruct(data.(fn{i})) + data.(fn{i}) = fixpnt(data.(fn{i}), recurse+1); + end + end +end diff --git a/@meeg/private/getset.m b/@meeg/private/getset.m index 2da4d308..ef49040f 100644 --- a/@meeg/private/getset.m +++ b/@meeg/private/getset.m @@ -5,7 +5,7 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: getset.m 5935 2014-03-28 15:59:57Z vladimir $ +% $Id: getset.m 6535 2015-08-25 11:45:26Z vladimir $ this = struct(this); @@ -25,7 +25,7 @@ res{i} = getfield(this, parent, {ind(i)}, fieldname); end - if isempty(res) || (all(cellfun('isclass', res, 'double') & ~cellfun('isempty', res))) + if isempty(res) || (all(cellfun('isclass', res, 'double') & (cellfun(@numel, res) == 1))) res = [res{:}]; end diff --git a/@meeg/selectchannels.m b/@meeg/selectchannels.m index cb2e22af..ef6d81f8 100644 --- a/@meeg/selectchannels.m +++ b/@meeg/selectchannels.m @@ -10,7 +10,7 @@ % Copyright (C) 2010-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: selectchannels.m 5675 2013-10-09 14:27:17Z vladimir $ +% $Id: selectchannels.m 6535 2015-08-25 11:45:26Z vladimir $ if ischar(channels) channels = {channels}; @@ -20,7 +20,8 @@ for i = 1:numel(channels) if ismember(upper(channels{i}), ... - {'ALL', 'EOG', 'ECG', 'EMG', 'EEG', 'MEG', 'MEGMAG', 'MEGGRAD', 'MEGPLANAR', 'MEGCOMB', 'REF', 'REFMAG', 'REFGRAD', 'LFP'}) + {'ALL','MEG', 'MEGPLANAR', 'MEGMAG', 'MEGGRAD', 'MEGCOMB','EEG',... + 'EOG', 'ECG', 'EMG', 'LFP', 'SRC', 'PHYS', 'ILAM', 'OTHER', 'REF', 'REFMAG', 'REFGRAD'}) chanind = [chanind indchantype(this, upper(channels{i}))]; elseif strncmpi('regexp_', channels{i}, 7) re = channels{i}(8:end); diff --git a/@meeg/sensors.m b/@meeg/sensors.m index 5fa960d3..0bc6c8b0 100644 --- a/@meeg/sensors.m +++ b/@meeg/sensors.m @@ -7,7 +7,7 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: sensors.m 6374 2015-03-12 10:15:18Z vladimir $ +% $Id: sensors.m 6535 2015-08-25 11:45:26Z vladimir $ if nargin<2 error('Sensor type (EEG or MEG) must be specified'); @@ -36,11 +36,22 @@ this.sensors(1).meg = newsens; res = check(this); end + case 'src' + if nargin < 3 + if isfield(this.sensors, 'src') + res = this.sensors.src; + else + res = []; + end + else + this.sensors(1).src = newsens; + res = check(this); + end otherwise error('Unsupported sensor type'); end -if nargin < 3 && ~isempty(res) && this.montage.Mind > 0 +if nargin < 3 && ~isempty(res) && ismember(lower(type), {'eeg', 'meg'}) && this.montage.Mind > 0 sens = res; montage = this.montage.M(this.montage.Mind); if ~isempty(intersect(sens.label, montage.labelorg)) diff --git a/@meeg/subsref.m b/@meeg/subsref.m index fbf933a7..fc1227dc 100644 --- a/@meeg/subsref.m +++ b/@meeg/subsref.m @@ -5,7 +5,7 @@ % Copyright (C) 2008-2013 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak, Stefan Kiebel -% $Id: subsref.m 5710 2013-10-22 14:01:23Z guillaume $ +% $Id: subsref.m 6600 2015-11-12 13:07:41Z christophe $ if isempty(subs) return; @@ -32,7 +32,9 @@ end if ischar(subs.subs{1}) % need to handle the case of a ':' argument - if ~strcmp(subs.subs{1},':'), error('This shouldn''t happen....'); end + if ~strcmp(subs.subs{1},':'), + error('This shouldn''t happen....'); + end if length(subs.subs) == 1 chanidx = 1:dim(1); subs.subs{2} = ':'; @@ -44,18 +46,21 @@ else chanidx = subs.subs{1}; end - - %check if correct channel index + + % check if correct channel index if any(chanidx > nchannels(this)) error('channel index higher than number of channels in current montage') end % get corresponding rows of 'tra' matrix traidx = this.montage.M(this.montage.Mind).tra(chanidx,:); - %change subs to use only the necessary channels from data + % change subs to use only the necessary channels from data lchan_o = find(any(traidx,1)); subs.subs{1} = lchan_o; + + % need to handle the case of ':' arguments if ischar(subs.subs{2}) if ~strcmp(subs.subs{2},':'), error('This shouldn''t happen....'); end + subs.subs{2} = 1:dim(2); Ntb = dim(2); else Ntb = length(subs.subs{2}); @@ -85,9 +90,9 @@ if ii mm +% +% Inputs +% XYZ - 3xN voxel coordinates of N blob values +% vals - N blob intensity values +% mat - 4x4 matrix specifying voxels -> mm % imgno - slice overlay img number to add to (defaults last in object) % % Outputs -% obj - modified object -% -% $Id: add_blobs.m,v 1.1 2005/04/20 15:05:36 matthewbrett Exp $ +% obj - modified object +%__________________________________________________________________________ + +% Matthew Brett +% $Id: add_blobs.m 6623 2015-12-03 18:38:08Z guillaume $ if nargin < 4 - error('Need all of object, xyz, vals, mat'); + error('Need all of object, xyz, vals, mat'); end if nargin < 5 - imgno = []; + imgno = []; end if isempty(imgno) - imgno = length(obj.img); + imgno = length(obj.img); end if ~isempty(xyz) - obj.img(imgno).vol = pr_blobs2vol(xyz,vals,mat); + obj.img(imgno).vol = pr_blobs2vol(xyz,vals,mat); end - diff --git a/@slover/add_matrix.m b/@slover/add_matrix.m index e91661be..8a3a5c15 100644 --- a/@slover/add_matrix.m +++ b/@slover/add_matrix.m @@ -1,30 +1,32 @@ function obj = add_matrix(obj, mat3d, mat, imgno) -% adds 3d matrix image vol to slice overlay -% FORMAT obj = add_matrix(imgno, mat3d, mat) +% Add 3d matrix image vol to slice overlay +% FORMAT obj = add_matrix(obj, mat3d, mat, imgno) % % Inputs % obj - object % mat3d - 3D matrix to add as img % mat - optional 4x4 voxel->world translation % imgno - optional img no to add to (defaults to last in object) -% +% % Ouputs % obj - modified object -% -% $Id: add_matrix.m,v 1.1 2005/04/20 15:05:36 matthewbrett Exp $ +%__________________________________________________________________________ + +% Matthew Brett +% $Id: add_matrix.m 6623 2015-12-03 18:38:08Z guillaume $ if nargin < 2 - return + return end if nargin < 3 - mat = []; + mat = []; end if nargin < 4 - imgno = []; + imgno = []; end if isempty(imgno) - imgno = length(obj.img); + imgno = length(obj.img); end if ~isempty(mat3d) - obj.img(imgno).vol = pr_matrix2vol(mat3d, mat); + obj.img(imgno).vol = pr_matrix2vol(mat3d, mat); end diff --git a/@slover/add_spm.m b/@slover/add_spm.m index a95edfa8..eaaee601 100644 --- a/@slover/add_spm.m +++ b/@slover/add_spm.m @@ -1,11 +1,13 @@ function obj = add_spm(obj,xSPM) -% Adds SPM blobs as new img to object, split effect, 'hot' colormap +% Add SPM blobs as new img to object, split effect, 'hot' colormap % FORMAT obj = add_spm(obj) -% +% % SPM results are fetched from the workspace -% -% $Id: add_spm.m,v 1.1 2005/04/20 15:05:36 matthewbrett Exp $ - +%__________________________________________________________________________ + +% Matthew Brett +% $Id: add_spm.m 6623 2015-12-03 18:38:08Z guillaume $ + if nargin == 2 XYZ = xSPM.XYZ; Z = xSPM.Z; @@ -14,8 +16,8 @@ [XYZ,Z,M] = pr_get_spm_results; end if isempty(XYZ) - warning('slover:noSPM', 'No SPM results to add'); - return + warning('slover:noSPM', 'No SPM results to add'); + return end newimg = length(obj.img)+1; diff --git a/@slover/display.m b/@slover/display.m index 30b0a2a7..661832b9 100644 --- a/@slover/display.m +++ b/@slover/display.m @@ -1,19 +1,21 @@ function display(obj) -% display method for slice overlay object -% -% $Id: display.m,v 1.1 2005/04/20 15:05:36 matthewbrett Exp $ - +% Display method for slice overlay object +%__________________________________________________________________________ + +% Matthew Brett +% $Id: display.m 6623 2015-12-03 18:38:08Z guillaume $ + X = struct(obj); src = '[slice overlay object]'; if isequal(get(0,'FormatSpacing'),'compact') - disp([inputname(1) ' =']); - disp(src); - disp(X) + disp([inputname(1) ' =']); + disp(src); + disp(X) else - disp(' ') - disp([inputname(1) ' =']); - disp(' '); - disp(src); - disp(' '); - disp(X) -end \ No newline at end of file + disp(' ') + disp([inputname(1) ' =']); + disp(' '); + disp(src); + disp(' '); + disp(X) +end \ No newline at end of file diff --git a/@slover/fill_defaults.m b/@slover/fill_defaults.m index c3e8ac0e..4d8347d9 100644 --- a/@slover/fill_defaults.m +++ b/@slover/fill_defaults.m @@ -1,109 +1,111 @@ function obj = fill_defaults(obj) -% check and fill fields in object +% Check and fill fields in object % FORMAT obj = fill_defaults(obj) -% +% % Input % obj - object to fill -% +% % Output % obj - object filled -% -% $Id: fill_defaults.m,v 1.2 2005/05/06 22:59:56 matthewbrett Exp $ +%__________________________________________________________________________ + +% Matthew Brett +% $Id: fill_defaults.m 6623 2015-12-03 18:38:08Z guillaume $ % Some default structures def_labs = struct('colour',[1 1 1],'size',0.075,'format', '%+3.0f'); def_fig = struct('position', [0 0 1 0.92], 'units', 'normalized', ... - 'valign', 'top'); + 'valign', 'top'); def_area = struct('position', [0 0 1 1], ... - 'units', '', ... - 'halign', 'center',... - 'valign', 'middle'); + 'units', '', ... + 'halign', 'center',... + 'valign', 'middle'); % Figure. We allow the figure to be dead, if we are going to resurrect % it later. dead_f = 0; if ~isempty(obj.figure) - % Is it dead? - if ~ishandle(obj.figure) - % Do we want to revive it? - if ~obj.resurrectf - error('Figure handle is not a valid figure') - end - dead_f = 1; - obj.refreshf = 1; - elseif ~strcmp(get(obj.figure,'Type'),'figure') - error('Figure handle is not a figure') - end + % Is it dead? + if ~ishandle(obj.figure) + % Do we want to revive it? + if ~obj.resurrectf + error('Figure handle is not a valid figure') + end + dead_f = 1; + obj.refreshf = 1; + elseif ~strcmp(get(obj.figure,'Type'),'figure') + error('Figure handle is not a figure') + end else - % no figure handle. Try spm figure, then gcf - obj.figure = spm_figure('FindWin', 'Graphics'); - if isempty(obj.figure) - obj.figure = gcf; - end + % no figure handle. Try spm figure, then gcf + obj.figure = spm_figure('FindWin', 'Graphics'); + if isempty(obj.figure) + obj.figure = gcf; + end end -% set defaults for SPM figure +% set defaults for SPM figure if ~dead_f - if strcmp(get(obj.figure, 'Tag'),'Graphics') - % position figure nicely for SPM - obj.area = mars_struct('fillafromb', obj.area, def_fig); - end + if strcmp(get(obj.figure, 'Tag'),'Graphics') + % position figure nicely for SPM + obj.area = mars_struct('fillafromb', obj.area, def_fig); + end end % orientation; string or 4x4 matrix if ischar(obj.transform) - orientn = find(strcmpi(obj.transform, {'axial', ... - 'coronal', ... - 'sagittal'})); - if isempty(orientn) - error('Unexpected orientation %s', obj.transform); - end - ts = [0 0 0 0 0 0 1 1 1;... - 0 0 0 pi/2 0 0 1 -1 1;... - 0 0 0 pi/2 0 -pi/2 -1 1 1]; - obj.transform = spm_matrix(ts(orientn,:)); + orientn = find(strcmpi(obj.transform, {'axial', ... + 'coronal', ... + 'sagittal'})); + if isempty(orientn) + error('Unexpected orientation %s', obj.transform); + end + ts = [0 0 0 0 0 0 1 1 1;... + 0 0 0 pi/2 0 0 1 -1 1;... + 0 0 0 pi/2 0 -pi/2 -1 1 1]; + obj.transform = spm_matrix(ts(orientn,:)); end % default slice size, slice matrix depends on orientation if (isempty(obj.slicedef) || isempty(obj.slices)) ... - && ~isempty(obj.img) - % take image sizes from first image - V = obj.img(1).vol; - D = V.dim(1:3); - T = obj.transform * V.mat; - vcorners = [1 1 1; D(1) 1 1; 1 D(2) 1; D(1:2) 1; ... - 1 1 D(3); D(1) 1 D(3); 1 D(2:3) ; D(1:3)]'; - corners = T * [vcorners; ones(1,8)]; - SC = sort(corners, 2); - vxsz = sqrt(sum(T(1:3,1:3).^2)); - - if isempty(obj.slicedef) - obj.slicedef = [SC(1,1) vxsz(1) SC(1,8);SC(2,1) vxsz(2) SC(2,8)]; - end - if isempty(obj.slices) - obj.slices = SC(3,1):vxsz(3):SC(3,8); - end + && ~isempty(obj.img) + % take image sizes from first image + V = obj.img(1).vol; + D = V.dim(1:3); + T = obj.transform * V.mat; + vcorners = [1 1 1; D(1) 1 1; 1 D(2) 1; D(1:2) 1; ... + 1 1 D(3); D(1) 1 D(3); 1 D(2:3) ; D(1:3)]'; + corners = T * [vcorners; ones(1,8)]; + SC = sort(corners, 2); + vxsz = sqrt(sum(T(1:3,1:3).^2)); + + if isempty(obj.slicedef) + obj.slicedef = [SC(1,1) vxsz(1) SC(1,8);SC(2,1) vxsz(2) SC(2,8)]; + end + if isempty(obj.slices) + obj.slices = SC(3,1):vxsz(3):SC(3,8); + end end % labels if ischar(obj.labels) - if ~strcmpi(obj.labels, 'none') - error('If labels is string, should be ''none'''); - end + if ~strcmpi(obj.labels, 'none') + error('If labels is string, should be ''none'''); + end else - obj.labels = mars_struct('fillafromb', obj.labels, def_labs); - dist = mean(diff(obj.slices)); - prec = ceil(-min(log10(dist), 0)); - obj.labels.format = sprintf('%%+3.%1df', prec); + obj.labels = mars_struct('fillafromb', obj.labels, def_labs); + dist = mean(diff(obj.slices)); + prec = ceil(-min(log10(dist), 0)); + obj.labels.format = sprintf('%%+3.%1df', prec); end % figure area stuff obj.area = mars_struct('fillafromb', obj.area, def_area); if isempty(obj.area.units) - if (all(obj.area.position>=0 & obj.area.position<=1)) - obj.area.units = 'normalized'; - else - obj.area.units = 'pixels'; - end + if (all(obj.area.position>=0 & obj.area.position<=1)) + obj.area.units = 'normalized'; + else + obj.area.units = 'pixels'; + end end % fill various img arguments @@ -111,76 +113,75 @@ % set colour intensities as we go remcol = 1; for i = 1:length(obj.img) - if ~mars_struct('isthere', obj.img(i), 'type') - % default is true colour, unless prop is Inf - obj.img(i).type = 'truecolour'; - if mars_struct('isthere', obj.img(i), 'prop') - if obj.img(i).prop == Inf - obj.img(i).type = 'split'; - obj.img(i).prop = 1; - end - end - end - if ~mars_struct('isthere', obj.img(i), 'hold') - if ~mars_struct('isthere', obj.img(i).vol, 'imgdata') - % normal file vol struct - obj.img(i).hold = 1; - else - % 3d matrix vol struct - obj.img(i).hold = 0; - end - end - if ~mars_struct('isthere', obj.img(i), 'background') - obj.img(i).background = NaN; - end - if ~mars_struct('isthere', obj.img(i), 'prop') - % default is true colour - if strcmpi(obj.img(i).type, 'truecolour') - obj.img(i).prop = remcol/(length(obj.img)-i+1); - remcol = remcol - obj.img(i).prop; - else - obj.img(i).prop = 1; - end - end - if ~mars_struct('isthere', obj.img(i), 'range') - [mx mn] = pr_volmaxmin(obj.img(i).vol); - obj.img(i).range = [mn mx]; - end - if ~mars_struct('isthere', obj.img(i), 'cmap') - if strcmpi(obj.img(i).type, 'split') - if obj.range(1)= minnpanels); - if isempty(tmp) - error('Whoops, cannot fit panels onto figure'); + + % calculate area of display in pixels + parea = obj.area.position; + if ~strcmp(obj.area.units, 'pixels') + ubu = get(obj.figure, 'units'); + set(obj.figure, 'units','pixels'); + tmp = get(obj.figure, 'Position'); + ascf = tmp(3:4); + if ~strcmp(obj.area.units, 'normalized') + set(obj.figure, 'units',obj.area.units); + tmp = get(obj.figure, 'Position'); + ascf = ascf ./ tmp(3:4); + end + set(figno, 'Units', ubu); + parea = parea .* repmat(ascf, 1, 2); end - b = tmp(1); % best fitting scaling - panels = panels(:,b); - axlen = axlen(:, b); - else - % if xslices is specified, assume X is flush with X figure dimensions - panels(X:Y,1) = [obj.xslices; 0]; - axlen(X:Y,1) = [asz(X)/panels(X); 0]; - end - - % Axis dimensions are in pixels. This prevents aspect ratio rescaling - panels(Y) = ceil(minnpanels/panels(X)); - axlen(Y) = axlen(X)*yxratio; - - % centre (etc) panels in display area as required - divs = [Inf 2 1];the_ds = [0;0]; - the_ds(X) = divs(strcmp(obj.area.halign, {'left','center','right'})); - the_ds(Y) = divs(strcmp(obj.area.valign, {'bottom','middle','top'})); - startc = parea(1:2)' + (asz'-(axlen.*panels))./the_ds; - - % make axes for panels - r=0;c=1; - npanels = prod(panels); - lastempty = npanels-cbars; - axisd = nan(1, npanels); - for i = 1:npanels - % panel userdata - if i<=nslices - u.type = 'slice'; - u.no = zmm(i); - elseif i > lastempty - u.type = 'cbar'; - u.no = i - lastempty; + asz = parea(3:4); + + % by default, make most parsimonious fit to figure + yxratio = length(ymm)*dims(Y,2)/(length(xmm)*dims(X,2)); + if isempty(obj.xslices) + % iteration needed to optimize, surprisingly. Thanks to Ian NS + axlen(X,:)=asz(1):-1:1; + axlen(Y,:)=yxratio*axlen(X,:); + panels = floor(asz'*ones(1,size(axlen,2))./axlen); + estnpanels = prod(panels); + tmp = find(estnpanels >= minnpanels); + if isempty(tmp) + error('Whoops, cannot fit panels onto figure'); + end + b = tmp(1); % best fitting scaling + panels = panels(:,b); + axlen = axlen(:, b); else - u.type = 'empty'; - u.no = i - nslices; + % if xslices is specified, assume X is flush with X figure dimensions + panels(X:Y,1) = [obj.xslices; 0]; + axlen(X:Y,1) = [asz(X)/panels(X); 0]; end - axpos = [r*axlen(X)+startc(X) (panels(Y)-c)*axlen(Y)+startc(Y) axlen']; - axisd(i) = axes(... - 'Parent',figno,... - 'XTick',[],... - 'XTickLabel',[],... - 'YTick',[],... - 'YTickLabel',[],... - 'Box','on',... - 'XLim',[1 vdims(X)],... - 'YLim',[1 vdims(Y)],... - 'Units', 'pixels',... - 'Position',axpos,... - 'Tag','slice overlay panel',... - 'UserData',u); - r = r+1; - if r >= panels(X) - r = 0; - c = c+1; + + % Axis dimensions are in pixels. This prevents aspect ratio rescaling + panels(Y) = ceil(minnpanels/panels(X)); + axlen(Y) = axlen(X)*yxratio; + + % centre (etc) panels in display area as required + divs = [Inf 2 1];the_ds = [0;0]; + the_ds(X) = divs(strcmp(obj.area.halign, {'left','center','right'})); + the_ds(Y) = divs(strcmp(obj.area.valign, {'bottom','middle','top'})); + startc = parea(1:2)' + (asz'-(axlen.*panels))./the_ds; + + % make axes for panels + r=0;c=1; + npanels = prod(panels); + lastempty = npanels-cbars; + axisd = nan(1, npanels); + for i = 1:npanels + % panel userdata + if i<=nslices + u.type = 'slice'; + u.no = zmm(i); + elseif i > lastempty + u.type = 'cbar'; + u.no = i - lastempty; + else + u.type = 'empty'; + u.no = i - nslices; + end + axpos = [r*axlen(X)+startc(X) (panels(Y)-c)*axlen(Y)+startc(Y) axlen']; + axisd(i) = axes(... + 'Parent',figno,... + 'XTick',[],... + 'XTickLabel',[],... + 'YTick',[],... + 'YTickLabel',[],... + 'Box','on',... + 'XLim',[1 vdims(X)],... + 'YLim',[1 vdims(Y)],... + 'Units', 'pixels',... + 'Position',axpos,... + 'Tag','slice overlay panel',... + 'UserData',u); + r = r+1; + if r >= panels(X) + r = 0; + c = c+1; + end end - end end % sort out labels if ischar(obj.labels) - do_labels = ~strcmpi(obj.labels, 'none'); + do_labels = ~strcmpi(obj.labels, 'none'); else - do_labels = 1; + do_labels = 1; end if do_labels - labels = obj.labels; - if iscell(labels.format) - if length(labels.format)~=vdims(Z) - error('Oh dear, expecting %d labels, but found %d', ... - vdims(Z), length(labels.contents)); - end - else - % format string for mm from AC labelling - fstr = labels.format; - labels.format = cell(vdims(Z),1); - acpt = obj.transform * [0 0 0 1]'; - for i = 1:vdims(Z) - labels.format(i) = {sprintf(fstr,zmm(i)-acpt(Z))}; + labels = obj.labels; + if iscell(labels.format) + if length(labels.format)~=vdims(Z) + error('Oh dear, expecting %d labels, but found %d', ... + vdims(Z), length(labels.contents)); + end + else + % format string for mm from AC labelling + fstr = labels.format; + labels.format = cell(vdims(Z),1); + acpt = obj.transform * [0 0 0 1]'; + for i = 1:vdims(Z) + labels.format(i) = {sprintf(fstr,zmm(i)-acpt(Z))}; + end end - end end % split images into picture and contour @@ -208,16 +210,16 @@ lrn = zeros(npimgs,3); cmaps = cell(npimgs); for i = 1:npimgs - cmaps(i)={obj.img(pictimgs(i)).cmap}; - lrnv = [obj.img(pictimgs(i)).outofrange, obj.img(pictimgs(i)).nancol]; - for j = 1:length(lrnv) - if numel(lrnv{j})==1 - lrn(i,j) = lrnv{j}; - else - cmaps(i) = {[cmaps{i}; lrnv{j}(1:3)]}; - lrn(i,j) = size(cmaps{i},1); + cmaps(i)={obj.img(pictimgs(i)).cmap}; + lrnv = [obj.img(pictimgs(i)).outofrange, obj.img(pictimgs(i)).nancol]; + for j = 1:length(lrnv) + if numel(lrnv{j})==1 + lrn(i,j) = lrnv{j}; + else + cmaps(i) = {[cmaps{i}; lrnv{j}(1:3)]}; + lrn(i,j) = size(cmaps{i},1); + end end - end end % cycle through slices displaying images @@ -226,149 +228,145 @@ zimg = zeros(pandims); for i = 1:nslices - ixyzmm = [x(:)';y(:)';ones(1,nvox)*zmm(i);ones(1,nvox)]; - img = zimg; - for j = 1:npimgs - thisimg = obj.img(pictimgs(j)); - i1 = sf_slice2panel(thisimg, ixyzmm, obj.transform, vdims); - % rescale to colormap - [csdata badvals]= pr_scaletocmap(... - i1,... - thisimg.range(1),... - thisimg.range(2),... - thisimg.cmap,... - lrn(j,:)); - % take indices from colormap to make true colour image - iimg = reshape(cmaps{j}(csdata(:),:),pandims); - tmp = repmat(logical(~badvals),[1 1 3]); - if strcmpi(thisimg.type, 'truecolour') - img(tmp) = img(tmp) + iimg(tmp)*thisimg.prop; - else % split colormap effect - img(tmp) = iimg(tmp)*thisimg.prop; - end - end - % threshold out of range values - img(img>1) = 1; - - image('Parent', axisd(i),... - 'ButtonDownFcn', obj.callback,... - 'CData',img); - - - % do contour plot - for j=1:length(contimgs) - thisimg = obj.img(contimgs(j)); - i1 = sf_slice2panel(thisimg, ixyzmm, obj.transform, vdims); - if any(any(isfinite(i1))) - i1(i1max(thisimg.range))=max(thisimg.range); - if ~any(diff(i1(isfinite(i1)))), continue, end % skip empty planes - if mars_struct('isthere', thisimg, 'linespec') - linespec = thisimg.linespec; - else - linespec = 'w-'; - end - set(axisd(i),'NextPlot','add'); - if mars_struct('isthere', thisimg, 'contours') - [c,h] = contour(axisd(i), i1, thisimg.contours, linespec); - else - [c,h] = contour(axisd(i), i1, linespec); - end - if ~isempty(h) - if ~mars_struct('isthere', thisimg, 'linespec') - % need to reset colours; contour assumes you just set the figure's - % colormap, but to overlay coloured contours on a greyscale image, - % one needs to have everything in truecolour, setting individual - % contour lines to their appropriate colour. - % (updated for MATLAB 7 and above) - convals = get(h, 'LevelList'); - if ~isempty(convals) - csdata = pr_scaletocmap(... - convals,... - thisimg.range(1),... - thisimg.range(2),... - thisimg.cmap,... - [1 size(thisimg.cmap,1) 1]); - colvals = thisimg.cmap(csdata(:),:)*thisimg.prop; - for ch = get(h, 'Children')' % (NB: transpose needed to loop) - CData = get(ch, 'CData'); % (CData is constant with final NaN) - colval = colvals(find(convals == CData(1), 1), :); - set(ch, 'EdgeColor', colval) + ixyzmm = [x(:)';y(:)';ones(1,nvox)*zmm(i);ones(1,nvox)]; + img = zimg; + for j = 1:npimgs + thisimg = obj.img(pictimgs(j)); + i1 = sf_slice2panel(thisimg, ixyzmm, obj.transform, vdims); + % rescale to colormap + [csdata,badvals]= pr_scaletocmap(... + i1,... + thisimg.range(1),... + thisimg.range(2),... + thisimg.cmap,... + lrn(j,:)); + % take indices from colormap to make true colour image + iimg = reshape(cmaps{j}(csdata(:),:),pandims); + tmp = repmat(logical(~badvals),[1 1 3]); + if strcmpi(thisimg.type, 'truecolour') + img(tmp) = img(tmp) + iimg(tmp)*thisimg.prop; + else % split colormap effect + img(tmp) = iimg(tmp)*thisimg.prop; end - end end - if mars_struct('isthere', thisimg, 'linewidth') - set(h, 'LineWidth', thisimg.linewidth); + % threshold out of range values + img(img>1) = 1; + + image('Parent', axisd(i),... + 'ButtonDownFcn', obj.callback,... + 'CData',img); + + + % do contour plot + for j=1:length(contimgs) + thisimg = obj.img(contimgs(j)); + i1 = sf_slice2panel(thisimg, ixyzmm, obj.transform, vdims); + if any(any(isfinite(i1))) + i1(i1max(thisimg.range))=max(thisimg.range); + if ~any(diff(i1(isfinite(i1)))), continue, end % skip empty planes + if mars_struct('isthere', thisimg, 'linespec') + linespec = thisimg.linespec; + else + linespec = 'w-'; + end + set(axisd(i),'NextPlot','add'); + if mars_struct('isthere', thisimg, 'contours') + [c,h] = contour(axisd(i), i1, thisimg.contours, linespec); + else + [c,h] = contour(axisd(i), i1, linespec); + end + if ~isempty(h) + if ~mars_struct('isthere', thisimg, 'linespec') + % need to reset colours; contour assumes you just set the figure's + % colormap, but to overlay coloured contours on a greyscale image, + % one needs to have everything in truecolour, setting individual + % contour lines to their appropriate colour. + % (updated for MATLAB 7 and above) + convals = get(h, 'LevelList'); + if ~isempty(convals) + csdata = pr_scaletocmap(... + convals,... + thisimg.range(1),... + thisimg.range(2),... + thisimg.cmap,... + [1 size(thisimg.cmap,1) 1]); + colvals = thisimg.cmap(csdata(:),:)*thisimg.prop; + for ch = get(h, 'Children')' % (NB: transpose needed to loop) + CData = get(ch, 'CData'); % (CData is constant with final NaN) + colval = colvals(find(convals == CData(1), 1), :); + set(ch, 'EdgeColor', colval) + end + end + end + if mars_struct('isthere', thisimg, 'linewidth') + set(h, 'LineWidth', thisimg.linewidth); + end + end + end end - end + + if do_labels + text('Parent',axisd(i),... + 'Color', labels.colour,... + 'FontUnits', 'normalized',... + 'VerticalAlignment','bottom',... + 'HorizontalAlignment','left',... + 'Position', [1 1],... + 'FontSize',labels.size,... + 'ButtonDownFcn', obj.callback,... + 'String', labels.format{i}); end - end - - if do_labels - text('Parent',axisd(i),... - 'Color', labels.colour,... - 'FontUnits', 'normalized',... - 'VerticalAlignment','bottom',... - 'HorizontalAlignment','left',... - 'Position', [1 1],... - 'FontSize',labels.size,... - 'ButtonDownFcn', obj.callback,... - 'String', labels.format{i}); - end end for i = (nslices+1):npanels - set(axisd(i),'Color',[0 0 0]); + set(axisd(i),'Color',[0 0 0]); end -% add colorbar(s) +% add colorbar(s) for i = 1:cbars - axno = axisd(end-cbars+i); - cbari = obj.img(obj.cbar(i)); - cml = size(cbari.cmap,1); - p = get(axno, 'Position'); % position of last axis - cw = p(3)*0.2; - ch = p(4)*0.75; - pc = p(3:4)/2; - [axlims idxs] = sort(cbari.range); - a=axes(... - 'Parent',figno,... - 'XTick',[],... - 'XTickLabel',[],... - 'Units', 'pixels',... - 'YLim', axlims,... - 'FontUnits', 'normalized',... - 'FontSize', 0.075,... - 'YColor',[1 1 1],... - 'Tag', 'cbar',... - 'Box', 'off',... - 'Position',[p(1)+pc(1)-cw/2,p(2)+pc(2)-ch/2,cw,ch]... - ); - image('Parent', a,... - 'YData', axlims(idxs),... - 'CData', reshape(cbari.cmap,[cml,1,3])); + axno = axisd(end-cbars+i); + cbari = obj.img(obj.cbar(i)); + cml = size(cbari.cmap,1); + p = get(axno, 'Position'); % position of last axis + cw = p(3)*0.2; + ch = p(4)*0.75; + pc = p(3:4)/2; + [axlims,idxs] = sort(cbari.range); + a=axes(... + 'Parent',figno,... + 'XTick',[],... + 'XTickLabel',[],... + 'Units', 'pixels',... + 'YLim', axlims,... + 'FontUnits', 'normalized',... + 'FontSize', 0.075,... + 'YColor',[1 1 1],... + 'Tag', 'cbar',... + 'Box', 'off',... + 'Position',[p(1)+pc(1)-cw/2,p(2)+pc(2)-ch/2,cw,ch]... + ); + image('Parent', a,... + 'YData', axlims(idxs),... + 'CData', reshape(cbari.cmap,[cml,1,3])); end % colourbars % Get stuff for figure, in case it dies later obj.figure_struct = mars_struct('split', get(figno), fig_struct_fields); -return - -% subfunctions -% ------------ +%========================================================================== function i1 = sf_slice2panel(img, xyzmm, transform, vdims) % to voxel space of image vixyz = (transform*img.vol.mat) \ xyzmm; -% raw data +% raw data if mars_struct('isthere', img.vol, 'imgdata') - V = img.vol.imgdata; + V = img.vol.imgdata; else - V = img.vol; + V = img.vol; end i1 = spm_sample_vol(V,vixyz(1,:),vixyz(2,:),vixyz(3,:), ... - [img.hold img.background]); + [img.hold img.background]); if mars_struct('isthere', img, 'func') - eval(img.func); + eval(img.func); end % transpose to reverse X and Y for figure i1 = reshape(i1, vdims(1:2))'; -return diff --git a/@slover/point_vals.m b/@slover/point_vals.m index aa7dcb6c..619966c0 100644 --- a/@slover/point_vals.m +++ b/@slover/point_vals.m @@ -1,24 +1,26 @@ function vals = point_vals(obj, XYZmm, holdlist) -% returns values from all the images at points given in XYZmm +% Return values from all the images at points given in XYZmm % FORMAT vals = point_vals(obj, XYZmm, holdlist) -% +% % (for the following, I is number of images in object, N is the number % of points to resample from) -% Input +% Input % obj - object -% XYZmm - 3xN XYZ natrix of points (in mm) -% holdlist - optional 1xI vector of resample hold values +% XYZmm - 3xN XYZ natrix of points (in mm) +% holdlist - optional 1xI vector of resample hold values % % Outputs % vals - IxN vector of values in images -% -% $Id: point_vals.m,v 1.1 2005/04/20 15:05:36 matthewbrett Exp $ - +%__________________________________________________________________________ + +% Matthew Brett +% $Id: point_vals.m 6623 2015-12-03 18:38:08Z guillaume $ + if nargin < 2 - error('Need XYZmm'); + error('Need XYZmm'); end if nargin < 3 - holdlist = [obj.img(:).hold]; + holdlist = [obj.img(:).hold]; end X=1;Y=2;Z=3; @@ -26,18 +28,16 @@ nvals = size(XYZmm,2); vals = zeros(nimgs,nvals)+NaN; if size(XYZmm,1)~=4 - XYZmm = [XYZmm(X:Z,:); ones(1,nvals)]; + XYZmm = [XYZmm(X:Z,:); ones(1,nvals)]; end for i = 1:nimgs - I = obj.img(i); - XYZ = I.vol.mat\XYZmm; - if ~mars_struct('isthere', I.vol, 'imgdata') - vol = I.vol; - else - vol = I.vol.imgdata; - end - vals(i,:) = spm_sample_vol(vol, XYZ(X,:), XYZ(Y,:),XYZ(Z,:),[holdlist(i) ... - I.background]); -end -return - + I = obj.img(i); + XYZ = I.vol.mat\XYZmm; + if ~mars_struct('isthere', I.vol, 'imgdata') + vol = I.vol; + else + vol = I.vol.imgdata; + end + vals(i,:) = spm_sample_vol(vol, XYZ(X,:), XYZ(Y,:),XYZ(Z,:),[holdlist(i) ... + I.background]); +end diff --git a/@slover/print_fig.m b/@slover/print_fig.m index 858249e8..c1687c5f 100644 --- a/@slover/print_fig.m +++ b/@slover/print_fig.m @@ -1,28 +1,30 @@ function print_fig(obj, filename, printstr) -% print slice overlay figure +% Print slice overlay figure % FORMAT print_fig(obj, filename, printstr) -% -% Input +% +% Input % obj - object % filename - optional filename to print to (obj.filename) % printstr - optional string giving print command (obj.printstr) % % Based on spm_figure print, and including fix from thence for ps % printing -% -% $Id: print_fig.m,v 1.1 2005/04/20 15:05:36 matthewbrett Exp $ - +%__________________________________________________________________________ + +% Matthew Brett +% $Id: print_fig.m 6623 2015-12-03 18:38:08Z guillaume $ + if nargin < 2 - filename = []; + filename = []; end if isempty(filename) - filename = obj.printfile; + filename = obj.printfile; end if nargin < 3 - printstr = ''; + printstr = ''; end if isempty(printstr) - printstr = obj.printstr; + printstr = obj.printstr; end %-Note current figure, & switch to figure to print @@ -45,18 +47,16 @@ function print_fig(obj, filename, printstr) tmp = [find(abs(errstr)==10),length(errstr)+1]; str = {errstr(1:tmp(1)-1)}; for i = 1:length(tmp)-1 - if tmp(i)+1 < tmp(i+1) + if tmp(i)+1 < tmp(i+1) str = [str, {errstr(tmp(i)+1:tmp(i+1)-1)}]; end end str = [str, '','- print command is:',[' ',printstr ' ' filename],... - '','- current directory is:',[' ',pwd],... - '',' * nothing has been printed *']; + '','- current directory is:',[' ',pwd],... + '',' * nothing has been printed *']; for i=1:length(str) - disp(str{i});end + disp(str{i});end end set(H,{'Units'},un) set(0,'CurrentFigure',cF) - -return diff --git a/@slover/private/mars_struct.m b/@slover/private/mars_struct.m index df8f416a..7055178e 100644 --- a/@slover/private/mars_struct.m +++ b/@slover/private/mars_struct.m @@ -1,13 +1,13 @@ function varargout = mars_struct(action, varargin) -% multifunction function for manipulating structures +% Multifunction function for manipulating structures % -% To help the exposition a bit: -% 'fill' in a name, means that values empty or missing +% To help the exposition a bit: +% 'fill' in a name, means that values empty or missing % in one structure are fetched from another -% +% % 'merge' means simply that missing fields are added, with % values, from a second structure (but not filled if empty) -% +% % Each function needs to deal with the case of empty arguments % % FORMAT c = mars_struct('fillafromb', a, b, fieldns, flags) @@ -20,7 +20,7 @@ % fields in b overwrite those in a) % flags may also contain 'r', which Restricts fields to write from b, to % those that are already present in a -% +% % FORMAT [c, d] = mars_struct('split', a, b) % split structure a into two, according to fields in b % so that c becomes a structure which contains the fields @@ -29,7 +29,7 @@ % or a cell array of fieldnames % % FORMAT [d] = mars_struct('strip', a, b) -% strips all fields present in b from those in a, +% strips all fields present in b from those in a, % returning denuded structure as d. b can be a structure % or a cell array of fieldnames. 'strip' is just 'split' % but returning only the second argument @@ -39,14 +39,14 @@ % % FORMAT [c,d] = mars_struct('ffillsplit', a, b) % force fill, followed by split -% All fields from a, that are also present in b, and not empty in b, -% are replaced with the values in b; the result is returned as c +% All fields from a, that are also present in b, and not empty in b, +% are replaced with the values in b; the result is returned as c % Any fields present in a, but not present in b, are returned in d % % FORMAT c = mars_struct('ffillmerge', a, b) % force fill followed by merge % performs 'ffillsplit' on a and b, then merges a and b -% All fields present in a or b are returned in c, but +% All fields present in a or b are returned in c, but % any fields present in both, now have the value from b % % FORMAT [c d] = mars_struct('splitmerge', a, b) @@ -61,7 +61,7 @@ % The call is recursive if more than two arguments are passed % Thus with structure s = struct('one', struct('two', 3)) % mars_struct('isthere', s, 'one', 'two') returns 1 -% +% % FORMAT z = mars_struct('getifthere', a, b [, c [, d ...]) % returns value of field named in b from a or [] if absent % Call is recursive, like 'isthere' above. @@ -69,193 +69,195 @@ % FORMAT strs = mars_struct('celldisp', a) % returns output like disp(a) as a cell array % Useful for printing text description of structure -% -% $Id: mars_struct.m,v 1.13 2004/09/22 16:02:38 matthewbrett Exp $ +%__________________________________________________________________________ + +% Matthew Brett +% $Id: mars_struct.m 6623 2015-12-03 18:38:08Z guillaume $ if nargin < 1 - error('Action needed'); + error('Action needed'); end if nargin < 2 - error('Must specify structure') + error('Must specify structure') end if nargin < 3 - varargin = {varargin{:} []}; + varargin = {varargin{:} []}; end -[a b] = deal(varargin{1:2}); +[a,b] = deal(varargin{1:2}); -switch lower(action) - case 'fillafromb' - % Return for empty passed args - if isempty(a), varargout = {b}; return, end - if isempty(b), varargout = {a}; return, end - if nargin < 4, fieldns = []; else fieldns = varargin{3}; end - if isempty(fieldns) - if ~isstruct(b), error('Need struct as 2nd argument'); end - fieldns = fieldnames(b); - end - if nargin < 5, flags = ''; else flags = varargin{4}; end - if isempty(flags), flags = ' ';end - - if ischar(fieldns), fieldns=cellstr(fieldns);end - - af = fieldnames(a)'; - bf = fieldns'; - - % classify fields 0 = a~b, 1 = a&b, 2=b~a - cf = af; - ftype = ismember(af, bf); - if ~any(flags == 'r') - b_not_a = find(~ismember(bf, af)); - cf = {cf{:} bf{b_not_a}}; - ftype = [ftype ones(1, length(b_not_a))*2]; - end - - % cope with arrays of structures - alen = prod(size(a)); - blen = prod(size(b)); - maxlen = max(alen, blen); - - for si=1:maxlen - ctmp = []; - for i=1:length(cf) - fn = cf{i}; - switch ftype(i) - case 0 % a~b - fval = getfield(a(si), fn); - case 1 % shared field - bfc = getfield(b(si), fn); - if isempty(getfield(a(si), fn)) || ... % a field is empty - (any(flags == 'f' & ~isempty(bfc)))% or force fill - fval = bfc; - else % field not empty, could be struct -> recurse - fval = getfield(a(si),fn); - if isstruct(fval) & isstruct(bfc) - fval = mars_struct('fillafromb',fval,bfc); - end - end - case 2 % b~a - fval = getfield(b(si), fn); - case 3 % no field information, see below - fval = []; - end - if isempty(ctmp) - ctmp = struct(fn, fval); - else - ctmp = setfield(ctmp, fn, fval); - end - end - c(si) = ctmp; - - if si == blen % reached end of bs, rest of b~a fields are empty - ftype = (ftype == 2) * 3; - elseif si == alen % end of a's rest of a~b fields are empty - ftype = (ftype == 0) * 2 + 1; - end - - end - varargout = {c}; - - case 'split' - if isempty(a), varargout = {a,a}; return, end - if isempty(b), varargout = {b,a}; return, end - d = a; - c = []; - - if ischar(b), b = {b};end - if isstruct(b), b = fieldnames(b);end - - for bf = b(:)' - if isfield(a, bf{1}) - c = setfield(c, bf{1}, getfield(a, bf{1})); - d = rmfield(d, bf{1}); - end - end - varargout = {c, d}; - - case 'strip' - [c d] = mars_struct('split', a, b); - varargout = {d}; - - case 'merge' - if isempty(a), varargout = {b}; return, end - if isempty(b), varargout = {a}; return, end - c = a; - - for bf = fieldnames(b)'; - if ~isfield(a, bf{1}) - c = setfield(c, bf{1}, getfield(b, bf{1})); - end - end - varargout = {c}; - - case 'ffillsplit' - if isempty(a) || isempty(b) - % Nothing in common, return unchanged - varargout = {a, b}; return - end - c = a; d = b; - - cf = fieldnames(c); - for i=1:length(cf) - if isfield(d, cf{i}) - dfc = getfield(d,cf{i}); - if ~isempty(dfc) - c = setfield(c, cf{i}, dfc); - end - d = rmfield(d, cf{i}); - end - end - varargout = {c,d}; - - case 'ffillmerge' - [a b] = mars_struct('ffillsplit', a, b); - varargout = {mars_struct('merge', a, b)}; - - case 'splitmerge' - [a c] = mars_struct('split', a, b); - varargout = {mars_struct('merge', a, b) c}; - - case 'isthere' - if isempty(a), varargout = {0}; return, end - c = mars_struct('getifthere', varargin{:}); - varargout = {~isempty(c)}; - - case 'getifthere' - if isempty(a), varargout = {[]}; return, end - if isempty(b), varargout = {[]}; return, end - for v = 2:nargin-1 - b = varargin{v}; - if ~isfield(a, b) - varargout = {[]}; - return - end - a = getfield(a, b); - end - varargout = {a}; - - case 'celldisp' - if isempty(a), varargout = {{}}; return, end - af = fieldnames(a); - c = {}; - pad_len = size(char(af), 2) + 4; - pad_str = ['%' num2str(pad_len) 's: %s']; - for f = 1:length(af) - d = getfield(a, af{f}); - cls = class(d); - sz = size(d); - szstr = sprintf('%dx', size(d)); - szstr(end) = []; - switch cls - case 'char' - case {'double', 'float'} - d = ['[' num2str(d) ']']; - otherwise - d = sprintf('[%s %s]', szstr, cls); - end - c{f} = sprintf(pad_str, af{f}, d); - end - varargout = {c}; - - otherwise - error(['Suspicious action was ' action]); +switch lower(action) + case 'fillafromb' + % Return for empty passed args + if isempty(a), varargout = {b}; return, end + if isempty(b), varargout = {a}; return, end + if nargin < 4, fieldns = []; else fieldns = varargin{3}; end + if isempty(fieldns) + if ~isstruct(b), error('Need struct as 2nd argument'); end + fieldns = fieldnames(b); + end + if nargin < 5, flags = ''; else flags = varargin{4}; end + if isempty(flags), flags = ' ';end + + if ischar(fieldns), fieldns=cellstr(fieldns);end + + af = fieldnames(a)'; + bf = fieldns'; + + % classify fields 0 = a~b, 1 = a&b, 2=b~a + cf = af; + ftype = ismember(af, bf); + if ~any(flags == 'r') + b_not_a = find(~ismember(bf, af)); + cf = {cf{:} bf{b_not_a}}; + ftype = [ftype ones(1, length(b_not_a))*2]; + end + + % cope with arrays of structures + alen = prod(size(a)); + blen = prod(size(b)); + maxlen = max(alen, blen); + + for si=1:maxlen + ctmp = []; + for i=1:length(cf) + fn = cf{i}; + switch ftype(i) + case 0 % a~b + fval = getfield(a(si), fn); + case 1 % shared field + bfc = getfield(b(si), fn); + if isempty(getfield(a(si), fn)) || ... % a field is empty + (any(flags == 'f' & ~isempty(bfc)))% or force fill + fval = bfc; + else % field not empty, could be struct -> recurse + fval = getfield(a(si),fn); + if isstruct(fval) && isstruct(bfc) + fval = mars_struct('fillafromb',fval,bfc); + end + end + case 2 % b~a + fval = getfield(b(si), fn); + case 3 % no field information, see below + fval = []; + end + if isempty(ctmp) + ctmp = struct(fn, fval); + else + ctmp = setfield(ctmp, fn, fval); + end + end + c(si) = ctmp; + + if si == blen % reached end of bs, rest of b~a fields are empty + ftype = (ftype == 2) * 3; + elseif si == alen % end of a's rest of a~b fields are empty + ftype = (ftype == 0) * 2 + 1; + end + + end + varargout = {c}; + + case 'split' + if isempty(a), varargout = {a,a}; return, end + if isempty(b), varargout = {b,a}; return, end + d = a; + c = []; + + if ischar(b), b = {b};end + if isstruct(b), b = fieldnames(b);end + + for bf = b(:)' + if isfield(a, bf{1}) + c = setfield(c, bf{1}, getfield(a, bf{1})); + d = rmfield(d, bf{1}); + end + end + varargout = {c, d}; + + case 'strip' + [c,d] = mars_struct('split', a, b); + varargout = {d}; + + case 'merge' + if isempty(a), varargout = {b}; return, end + if isempty(b), varargout = {a}; return, end + c = a; + + for bf = fieldnames(b)'; + if ~isfield(a, bf{1}) + c = setfield(c, bf{1}, getfield(b, bf{1})); + end + end + varargout = {c}; + + case 'ffillsplit' + if isempty(a) || isempty(b) + % Nothing in common, return unchanged + varargout = {a, b}; return + end + c = a; d = b; + + cf = fieldnames(c); + for i=1:length(cf) + if isfield(d, cf{i}) + dfc = getfield(d,cf{i}); + if ~isempty(dfc) + c = setfield(c, cf{i}, dfc); + end + d = rmfield(d, cf{i}); + end + end + varargout = {c,d}; + + case 'ffillmerge' + [a,b] = mars_struct('ffillsplit', a, b); + varargout = {mars_struct('merge', a, b)}; + + case 'splitmerge' + [a,c] = mars_struct('split', a, b); + varargout = {mars_struct('merge', a, b) c}; + + case 'isthere' + if isempty(a), varargout = {0}; return, end + c = mars_struct('getifthere', varargin{:}); + varargout = {~isempty(c)}; + + case 'getifthere' + if isempty(a), varargout = {[]}; return, end + if isempty(b), varargout = {[]}; return, end + for v = 2:nargin-1 + b = varargin{v}; + if ~isfield(a, b) + varargout = {[]}; + return + end + a = getfield(a, b); + end + varargout = {a}; + + case 'celldisp' + if isempty(a), varargout = {{}}; return, end + af = fieldnames(a); + c = {}; + pad_len = size(char(af), 2) + 4; + pad_str = ['%' num2str(pad_len) 's: %s']; + for f = 1:length(af) + d = getfield(a, af{f}); + cls = class(d); + sz = size(d); + szstr = sprintf('%dx', size(d)); + szstr(end) = []; + switch cls + case 'char' + case {'double', 'float'} + d = ['[' num2str(d) ']']; + otherwise + d = sprintf('[%s %s]', szstr, cls); + end + c{f} = sprintf(pad_str, af{f}, d); + end + varargout = {c}; + + otherwise + error(['Suspicious action was ' action]); end % switch diff --git a/@slover/private/pr_basic_ui.m b/@slover/private/pr_basic_ui.m index 02ce17d4..5294e0a4 100644 --- a/@slover/private/pr_basic_ui.m +++ b/@slover/private/pr_basic_ui.m @@ -7,23 +7,25 @@ % imgs - string or cell array of image names to display % (defaults to GUI select if no arguments passed) % dispf - optional flag: if set, displays overlay (default = 1) -% -% $Id: pr_basic_ui.m,v 1.1 2005/04/20 15:05:00 matthewbrett Exp $ - +%__________________________________________________________________________ + +% Matthew Brett +% $Id: pr_basic_ui.m 6623 2015-12-03 18:38:08Z guillaume $ + if nargin < 1 - imgs = ''; + imgs = ''; end if isempty(imgs) - [imgs, sts] = spm_select([1 Inf], 'image', 'Image(s) to display'); - if ~sts, obj=[]; return; end + [imgs, sts] = spm_select([1 Inf], 'image', 'Image(s) to display'); + if ~sts, obj=[]; return; end end if ischar(imgs) - imgs = cellstr(imgs); + imgs = cellstr(imgs); end if nargin < 2 - dispf = 1; + dispf = 1; end - + spm_input('!SetNextPos', 1); % load images @@ -41,78 +43,78 @@ deftype = 1; obj.cbar = []; for i = 1:nimgs - obj.img(i).vol = spm_vol(imgs{i}); - options = {'Structural','Truecolour', ... - 'Blobs','Negative blobs','Contours'}; - % if there are SPM results in the workspace, add this option - [XYZ Z M] = pr_get_spm_results; - if ~isempty(XYZ) - options = {'Structural with SPM blobs', options{:}}; - end - itype = spm_input(sprintf('Img %d: %s - image type?', i, imgns{i}), '+1', ... - 'm', char(options),options, deftype); - imgns(i) = {sprintf('Img %d (%s)',i,itype{1})}; - [mx mn] = slover('volmaxmin', obj.img(i).vol); - if ~isempty(strmatch('Structural', itype)) - obj.img(i).type = 'truecolour'; - obj.img(i).cmap = gray; - obj.img(i).range = [mn mx]; - deftype = 2; - cscale = [cscale i]; - if strcmp(itype,'Structural with SPM blobs') - obj = add_spm(obj); + obj.img(i).vol = spm_vol(imgs{i}); + options = {'Structural','Truecolour', ... + 'Blobs','Negative blobs','Contours'}; + % if there are SPM results in the workspace, add this option + [XYZ,Z,M] = pr_get_spm_results; + if ~isempty(XYZ) + options = {'Structural with SPM blobs', options{:}}; end - else - cprompt = ['Colormap: ' imgns{i}]; - switch itype{1} - case 'Truecolour' - obj.img(i).type = 'truecolour'; - dcmap = 'flow.lut'; - drange = [mn mx]; - cscale = [cscale i]; - obj.cbar = [obj.cbar i]; - case 'Blobs' - obj.img(i).type = 'split'; - dcmap = 'hot'; - drange = [0 mx]; - obj.img(i).prop = 1; - obj.cbar = [obj.cbar i]; - case 'Negative blobs' - obj.img(i).type = 'split'; - dcmap = 'winter'; - drange = [0 mn]; - obj.img(i).prop = 1; - obj.cbar = [obj.cbar i]; - case 'Contours' - obj.img(i).type = 'contour'; - dcmap = 'white'; - drange = [mn mx]; - obj.img(i).prop = 1; + itype = spm_input(sprintf('Img %d: %s - image type?', i, imgns{i}), '+1', ... + 'm', char(options),options, deftype); + imgns(i) = {sprintf('Img %d (%s)',i,itype{1})}; + [mx,mn] = slover('volmaxmin', obj.img(i).vol); + if ~isempty(strmatch('Structural', itype)) + obj.img(i).type = 'truecolour'; + obj.img(i).cmap = gray; + obj.img(i).range = [mn mx]; + deftype = 2; + cscale = [cscale i]; + if strcmp(itype,'Structural with SPM blobs') + obj = add_spm(obj); + end + else + cprompt = ['Colormap: ' imgns{i}]; + switch itype{1} + case 'Truecolour' + obj.img(i).type = 'truecolour'; + dcmap = 'flow.lut'; + drange = [mn mx]; + cscale = [cscale i]; + obj.cbar = [obj.cbar i]; + case 'Blobs' + obj.img(i).type = 'split'; + dcmap = 'hot'; + drange = [0 mx]; + obj.img(i).prop = 1; + obj.cbar = [obj.cbar i]; + case 'Negative blobs' + obj.img(i).type = 'split'; + dcmap = 'winter'; + drange = [0 mn]; + obj.img(i).prop = 1; + obj.cbar = [obj.cbar i]; + case 'Contours' + obj.img(i).type = 'contour'; + dcmap = 'white'; + drange = [mn mx]; + obj.img(i).prop = 1; + end + obj.img(i).cmap = sf_return_cmap(cprompt, dcmap); + obj.img(i).range = spm_input('Img val range for colormap','+1', 'e', drange, 2); end - obj.img(i).cmap = sf_return_cmap(cprompt, dcmap); - obj.img(i).range = spm_input('Img val range for colormap','+1', 'e', drange, 2); - end end ncmaps=length(cscale); if ncmaps == 1 - obj.img(cscale).prop = 1; + obj.img(cscale).prop = 1; else - remcol=1; - for i = 1:ncmaps - ino = cscale(i); - obj.img(ino).prop = spm_input(sprintf('%s intensity',imgns{ino}),... - '+1', 'e', ... - remcol/(ncmaps-i+1),1); - remcol = remcol - obj.img(ino).prop; - end + remcol=1; + for i = 1:ncmaps + ino = cscale(i); + obj.img(ino).prop = spm_input(sprintf('%s intensity',imgns{ino}),... + '+1', 'e', ... + remcol/(ncmaps-i+1),1); + remcol = remcol - obj.img(ino).prop; + end end - + obj.transform = deblank(spm_input('Image orientation', '+1', ['Axial|' ... - ' Coronal|Sagittal'], strvcat('axial','coronal','sagittal'), ... - 1)); + ' Coronal|Sagittal'], strvcat('axial','coronal','sagittal'), ... + 1)); % use SPM figure window -obj.figure = spm_figure('GetWin', 'Graphics'); +obj.figure = spm_figure('GetWin', 'Graphics'); % slices for display obj = fill_defaults(obj); @@ -120,24 +122,20 @@ dist = mean(diff(slices)); prec = ceil(-min(log10(dist), 0)); obj.slices = spm_input('Slices to display (mm)', '+1', 'e', ... - sprintf('%0.*f:%0.*f:%0.*f',... - prec, slices(1),... - prec, dist,... - prec, slices(end))... - ); + sprintf('%0.*f:%0.*f:%0.*f',... + prec, slices(1),... + prec, dist,... + prec, slices(end))... + ); % and do the display if dispf, obj = paint(obj); end -return - -% Subfunctions -% ------------ +%========================================================================== function cmap = sf_return_cmap(prompt,defmapn) cmap = []; while isempty(cmap) - [cmap w]= slover('getcmap', spm_input(prompt,'+1','s', defmapn)); - if isempty(cmap), disp(w);end + [cmap,w]= slover('getcmap', spm_input(prompt,'+1','s', defmapn)); + if isempty(cmap), disp(w);end end -return diff --git a/@slover/private/pr_blobs2vol.m b/@slover/private/pr_blobs2vol.m index 9fc647db..6edd42c2 100644 --- a/@slover/private/pr_blobs2vol.m +++ b/@slover/private/pr_blobs2vol.m @@ -1,30 +1,26 @@ function vol = pr_blobs2vol(xyz,vals,mat) -% takes XYZ matrix and values, returns SPM matrix vol struct +% Take XYZ matrix and values and return SPM matrix vol struct % FORMAT vol = pr_blobs2vol(xyz,vals,mat) -% -% Inputs +% +% Inputs % xyz - 3xN X Y Z coordinate matrix (in voxels) % vals - 1xN values, one per coordinate % mat - 4x4 voxel->world space transformation -% +% % Outputs % vol - vol struct, with matrix data 'imgdata' field -% -% $Id: pr_blobs2vol.m,v 1.1 2005/04/20 15:05:00 matthewbrett Exp $ - -if nargin < 3 - error('Need XYZ, vals and mat'); -end +%__________________________________________________________________________ + +% Matthew Brett +% $Id: pr_blobs2vol.m 6623 2015-12-03 18:38:08Z guillaume $ vol = []; if ~isempty(xyz), - rcp = round(xyz); - vol.dim = max(rcp,[],2)'; - off = rcp(1,:) + vol.dim(1)*(rcp(2,:)-1+vol.dim(2)*(rcp(3,:)-1)); - vol.imgdata = zeros(vol.dim)+NaN; - vol.imgdata(off) = vals; - vol.imgdata = reshape(vol.imgdata,vol.dim); - vol.mat = mat; + rcp = round(xyz); + vol.dim = max(rcp,[],2)'; + off = rcp(1,:) + vol.dim(1)*(rcp(2,:)-1+vol.dim(2)*(rcp(3,:)-1)); + vol.imgdata = zeros(vol.dim)+NaN; + vol.imgdata(off) = vals; + vol.imgdata = reshape(vol.imgdata,vol.dim); + vol.mat = mat; end -return - diff --git a/@slover/private/pr_get_spm_results.m b/@slover/private/pr_get_spm_results.m index 60ebf681..0c17b3f4 100644 --- a/@slover/private/pr_get_spm_results.m +++ b/@slover/private/pr_get_spm_results.m @@ -1,31 +1,22 @@ -function [XYZ, Z, M] = pr_get_spm_results; -% fetches SPM results, returns as point list -% FORMAT [XYZ, Z, M] = pr_get_spm_results; -% +function [XYZ, Z, M] = pr_get_spm_results +% Fetch SPM results and return as point list +% FORMAT [XYZ, Z, M] = pr_get_spm_results +% % Outputs % XYZ - XYZ point list in voxels (empty if not found) % Z - values at points in XYZ -% M - 4x4 voxel -> world transformation matrix -% -% $Id: pr_get_spm_results.m,v 1.2 2005/05/06 22:57:40 matthewbrett Exp $ - +% M - 4x4 voxel -> world transformation matrix +%__________________________________________________________________________ + +% Matthew Brett +% $Id: pr_get_spm_results.m 6623 2015-12-03 18:38:08Z guillaume $ + errstr = '''Cannot find SPM results in workspace'''; -[XYZ Z M] = deal([]); +[XYZ,Z,M] = deal([]); -V = spm('ver'); -switch V(4:end) - case '99' - have_res = evalin('base', 'exist(''SPM'', ''var'')'); - if ~have_res, return, end - SPM = evalin('base', 'SPM', ['error(' errstr ')']); - XYZ = SPM.XYZ; - Z = SPM.Z; - M = evalin('base', 'VOL.M', ['error(' errstr ')']); - otherwise - have_res = evalin('base', 'exist(''xSPM'', ''var'')'); - if ~have_res, return, end - xSPM = evalin('base', 'xSPM', ['error(' errstr ')']); - XYZ = xSPM.XYZ; - Z = xSPM.Z; - M = xSPM.M; -end +have_res = evalin('base', 'exist(''xSPM'', ''var'')'); +if ~have_res, return, end +xSPM = evalin('base', 'xSPM', ['error(' errstr ')']); +XYZ = xSPM.XYZ; +Z = xSPM.Z; +M = xSPM.M; diff --git a/@slover/private/pr_getcmap.m b/@slover/private/pr_getcmap.m index 33e45f8d..b727ddbe 100644 --- a/@slover/private/pr_getcmap.m +++ b/@slover/private/pr_getcmap.m @@ -1,7 +1,7 @@ function [cmap, warnstr] = pr_getcmap(acmapname) -% get colormap of name acmapname +% Get colormap of name acmapname % FORMAT [cmap, warnstr] = pr_getcmap(acmapname) -% +% % Inputs % acmapname - string. Can be (in order of precedence) % - matrix name in base workspace @@ -14,97 +14,98 @@ % cmap - Nx3 colormap matrix % or empty if fails % warnstr - warning message if fails -% -% $Id: pr_getcmap.m,v 1.1 2005/04/20 15:05:00 matthewbrett Exp $ +%__________________________________________________________________________ + +% Matthew Brett +% $Id: pr_getcmap.m 6623 2015-12-03 18:38:08Z guillaume $ -cmap = []; warnstr = []; +cmap = []; warnstr = []; if nargin < 1 - acmapname = ''; + acmapname = ''; end if isempty(acmapname) - warnstr = 'No colormap name passed'; - return + warnstr = 'No colormap name passed'; + return end % try a matrix first cmap = evalin('base',acmapname,'[]'); if ~isempty(cmap) - if size(cmap, 2)~=3 - warnstr = ['Colormap matrix ' acmapname ' was not N by 3']; - cmap = []; - end - return + if size(cmap, 2)~=3 + warnstr = ['Colormap matrix ' acmapname ' was not N by 3']; + cmap = []; + end + return end % not a matrix, is it... % a colour name? tmp = strcmpi(acmapname, {'red','green','blue','cyan', 'magenta', ... - 'yellow', 'black', 'white'}); + 'yellow', 'black', 'white'}); coldefs = [1 0 0; - 0 1 0; - 0 0 1; - 0 1 1; - 1 0 1; - 1 1 0; - 0 0 0; - 1 1 1]; + 0 1 0; + 0 0 1; + 0 1 1; + 1 0 1; + 1 1 0; + 0 0 0; + 1 1 1]; if any(tmp) - coldef = coldefs(tmp, :); - if ~any(diff(coldef)) - cmap = coldef; - else - cmap = (0:63)' * coldef / 63; - end - return + coldef = coldefs(tmp, :); + if ~any(diff(coldef)) + cmap = coldef; + else + cmap = (0:63)' * coldef / 63; + end + return end % is it a file? oname = acmapname; -[p f e] = fileparts(acmapname); +[p,f,e] = fileparts(acmapname); % if no extension, add .mat -if isempty(e) - e = '.mat'; - acmapname = fullfile(p, [f e]); +if isempty(e) + e = '.mat'; + acmapname = fullfile(p, [f e]); end ef = exist(acmapname, 'file'); % file doesn't exist? Try home directory of this mfile if ~ef - mfp = fileparts(which(mfilename)); - acmapname = fullfile(mfp, [f e]); - ef = exist(acmapname, 'file'); -end + mfp = fileparts(which(mfilename)); + acmapname = fullfile(mfp, [f e]); + ef = exist(acmapname, 'file'); +end if ~ef - warnstr = ['No matrix or file ''' oname '''']; - return + warnstr = ['No matrix or file ''' oname '''']; + return end % found file, get cmap switch lower(e) - case '.mat' - % try for matrix of same name - s = load(acmapname); - if isfield(s, f) - cmap = getfield(s, f); - else % get first matrix in mat file - s = struct2cell(s); - cmap = s{1}; - end - if size(cmap, 2)~=3 - warnstr = ['Colormap from ' acmapname ' was not an N by 3 matrix']; - cmap = []; - end - case '.lut' - fid = fopen(acmapname, 'rb'); - if fid~=-1 - cmap = fread(fid, Inf); - l = length(cmap); - if ~rem(l,3) - cmap = reshape(cmap, l/3, 3) / 255; - else - warnstr = ['LUT map ' acmapname ' was wrong size']; - end - fclose(fid); - else - warnstr = ['Cannot open LUT colormap file ' acmapname]; - end - otherwise - warnstr = ['Unrecognized file extension ' e ' for ' acmapname]; + case '.mat' + % try for matrix of same name + s = load(acmapname); + if isfield(s, f) + cmap = getfield(s, f); + else % get first matrix in mat file + s = struct2cell(s); + cmap = s{1}; + end + if size(cmap, 2)~=3 + warnstr = ['Colormap from ' acmapname ' was not an N by 3 matrix']; + cmap = []; + end + case '.lut' + fid = fopen(acmapname, 'rb'); + if fid~=-1 + cmap = fread(fid, Inf); + l = length(cmap); + if ~rem(l,3) + cmap = reshape(cmap, l/3, 3) / 255; + else + warnstr = ['LUT map ' acmapname ' was wrong size']; + end + fclose(fid); + else + warnstr = ['Cannot open LUT colormap file ' acmapname]; + end + otherwise + warnstr = ['Unrecognized file extension ' e ' for ' acmapname]; end -return diff --git a/@slover/private/pr_matrix2vol.m b/@slover/private/pr_matrix2vol.m index 60b8c503..12a52542 100644 --- a/@slover/private/pr_matrix2vol.m +++ b/@slover/private/pr_matrix2vol.m @@ -1,28 +1,30 @@ function vol = pr_matrix2vol(mat3d, mat) -% returns (pseudo) vol struct for 3d matrix +% Return (pseudo) vol struct for 3d matrix % FORMAT vol = pr_matrix2vol(mat3d,mat) % % Inputs % mat3d - 3D matrix % mat - optional 4x4 voxel -> world transformation -% +% % Outputs % vol - kind of SPM vol struct with matrix data added -% -% $Id: pr_matrix2vol.m,v 1.1 2005/04/20 15:05:00 matthewbrett Exp $ - +%__________________________________________________________________________ + +% Matthew Brett +% $Id: pr_matrix2vol.m 6623 2015-12-03 18:38:08Z guillaume $ + if nargin < 1 - error('Need matrix to add to vol'); + error('Need matrix to add to vol'); end if nargin < 2 - mat = []; + mat = []; end if isempty(mat) - mat = spm_matrix([]); + mat = spm_matrix([]); end vol = []; if ~isempty(mat3d) - vol.imgdata = mat3d; - vol.mat = mat; - vol.dim = size(mat3d); + vol.imgdata = mat3d; + vol.mat = mat; + vol.dim = size(mat3d); end diff --git a/@slover/private/pr_scaletocmap.m b/@slover/private/pr_scaletocmap.m index 89a96dba..f1978e63 100644 --- a/@slover/private/pr_scaletocmap.m +++ b/@slover/private/pr_scaletocmap.m @@ -1,7 +1,7 @@ function [img, badvals]=pr_scaletocmap(inpimg,mn,mx,cmap,lrn) -% scales image data to colormap, returning colormap indices +% Scale image data to colormap, returning colormap indices % FORMAT [img, badvals]=pr_scaletocmap(inpimg,mn,mx,cmap,lrn) -% +% % Inputs % inpimg - matrix containing image to scale % mn - image value that maps to first value of colormap @@ -13,39 +13,40 @@ % - lrn(3) (N=NaN) - NaN values % If lrn value is 0, then colormap values are set to 1, and % indices to these values are returned in badvals (below) -% +% % Output % img - inpimg scaled between 1 and (size(cmap, 1)) % badvals - indices into inpimg containing values out of range, as % specified by lrn vector above -% -% $Id: pr_scaletocmap.m,v 1.1 2005/04/20 15:05:00 matthewbrett Exp $ +%__________________________________________________________________________ + +% Matthew Brett +% $Id: pr_scaletocmap.m 6623 2015-12-03 18:38:08Z guillaume $ if nargin < 4 - error('Need inpimg, mn, mx, and cmap'); + error('Need inpimg, mn, mx, and cmap'); end cml = size(cmap,1); if nargin < 5 - lrn = [1 cml 0]; + lrn = [1 cml 0]; end img = (inpimg-mn)/(mx-mn); % img normalized to mn=0,mx=1 if cml==1 % values between 0 and 1 -> 1 - img(img>=0 & img<=1)=1; + img(img>=0 & img<=1)=1; else - img = img*(cml-1)+1; + img = img*(cml-1)+1; end outvals = {img<1, img>cml, isnan(img)}; img= round(img); badvals = zeros(size(img)); for i = 1:length(lrn) - if lrn(i) - img(outvals{i}) = lrn(i); - else - badvals = badvals | outvals{i}; - img(outvals{i}) = 1; - end + if lrn(i) + img(outvals{i}) = lrn(i); + else + badvals = badvals | outvals{i}; + img(outvals{i}) = 1; + end end -return diff --git a/@slover/private/pr_volmaxmin.m b/@slover/private/pr_volmaxmin.m index 537a57ea..db6eb734 100644 --- a/@slover/private/pr_volmaxmin.m +++ b/@slover/private/pr_volmaxmin.m @@ -1,39 +1,39 @@ function [mx,mn] = pr_volmaxmin(vol) -% returns max and min value in image volume +% Return max and min value in image volume % FORMAT [mx,mn] = pr_volmaxmin(vol) -% +% % Input % vol - image name or vol struct -% +% % Outputs % mx - maximum % mn - minimum -% -% $Id: pr_volmaxmin.m,v 1.1 2005/04/20 15:05:00 matthewbrett Exp $ +%__________________________________________________________________________ + +% Matthew Brett +% $Id: pr_volmaxmin.m 6623 2015-12-03 18:38:08Z guillaume $ if nargin < 1 - error('Need volume to process'); + error('Need volume to process'); end if ischar(vol) - vol = spm_vol(vol); + vol = spm_vol(vol); end if ~isstruct(vol) - error('vol did not result in vol struct'); + error('vol did not result in vol struct'); end if mars_struct('isthere', vol, 'imgdata') - tmp = vol.imgdata(isfinite(vol.imgdata)); - mx = max(tmp); - mn = min(tmp); + tmp = vol.imgdata(isfinite(vol.imgdata)); + mx = max(tmp); + mn = min(tmp); else mx = -Inf;mn=Inf; for i=1:vol.dim(3), - tmp = spm_slice_vol(vol,spm_matrix([0 0 i]),vol.dim(1:2),[0 NaN]); - tmp = tmp(find(isfinite(tmp(:)))); - if ~isempty(tmp) - mx = max([mx; tmp]); - mn = min([mn; tmp]); - end + tmp = spm_slice_vol(vol,spm_matrix([0 0 i]),vol.dim(1:2),[0 NaN]); + tmp = tmp(find(isfinite(tmp(:)))); + if ~isempty(tmp) + mx = max([mx; tmp]); + mn = min([mn; tmp]); + end end end -return - diff --git a/@slover/slover.m b/@slover/slover.m index ce5a1946..1b9a56c4 100644 --- a/@slover/slover.m +++ b/@slover/slover.m @@ -2,8 +2,8 @@ % class constructor for slice overlay (slover) object % FORMAT [o, others] = slover(params, others, varargin) % -% Inputs -% params - either: +% Inputs +% params - either: % - action string implementing class methods (see below) % - array of image names / vol structs to display % - structure with some fields for object (see below) @@ -18,7 +18,7 @@ % - img - array of structs with information for images to display % - img structs contain fields % type - one of {'truecolour' 'split', 'contour'}; -% truecolour - displays transparent (see prop) image +% truecolour - displays transparent (see prop) image % overlaid with any previous % split - in defined area, replaces image present (SPM % type activation display) @@ -30,7 +30,7 @@ % cmap - colormap for this image % nancol - color for NaN. If scalar, this is an index into % the image cmap. If 1x3 vector, it's a colour -% prop - proportion of intensity for this cmap/img +% prop - proportion of intensity for this cmap/img % func - function to apply to image before scaling to cmap % (and therefore before min/max thresholding. E.g. a func of % 'i1(i1==0)=NaN' would convert zeros to NaNs @@ -43,14 +43,14 @@ % colormap values < 1, i.e for image values < % range(1), if (range(1) % range(1) where (range(1)>range(2)). If missing, -% display min (for Left) and max (for Right) value from colormap. +% display min (for Left) and max (for Right) value from colormap. % Otherwise should be a 2 element cell array, where % the first element is the colour value for image values % left of 'range', and the second is for image values % right of 'range'. Scalar values for % colour index the colormap, 3x1 vectors are colour % values. An empty array attracts default settings -% appropriate to the mode - i.e. transparent colour (where +% appropriate to the mode - i.e. transparent colour (where % img(n).type is truecolour), or split colour. Empty cells % default to 0. 0 specifies that voxels with this % colour do not influence the image (split = @@ -61,11 +61,11 @@ % NaN % linespec - string, applies only to contour map, % e.g. 'w-' for white continuous lines -% contours - vector, applies to contour map only, defines -% values in image for which to show contours +% contours - vector, applies to contour map only, defines +% values in image for which to show contours % (see help contours) % linewidth - scalar, width in points of contour lines -% +% % - transform - either - 4x4 transformation to apply to image slice position, % relative to mm given by slicedef, before display % or - text string, one of axial, coronal, sagittal @@ -98,15 +98,15 @@ % halign - one of left,{center},right % valign - one of top,{middle},bottom % - xslices - no of slices to display across figure (defaults to an optimum) -% - cbar - if empty, missing, no colourbar. If an array of integers, then +% - cbar - if empty, missing, no colourbar. If an array of integers, then % indexes img array, and makes colourbar for each cmap for % that img. Cbars specified in order of appearance L->R % - labels - struct can be: % - empty (-> default numerical labels) -% - 'none' (string) (no labels) +% - 'none' (string) (no labels) % - or contain fields: -% colour - colour for label text -% size - font size in units normalized to slice axes +% colour - colour for label text +% size - font size in units normalized to slice axes % format - if = cell array of strings = % labels for each slice in Z. If is string, specifies % sprintf format string for labelling in distance of the @@ -119,9 +119,9 @@ % mm of the position of a mouse click on one of the image % slices, set callback to: % 'get_pos(get(gcf, ''UserData''))' -% To print the intensity values of the images at the clicked point: +% To print the intensity values of the images at the clicked point: % ['so_obj = get(gcf, ''UserData''); ' ... -% 'point_vals(so_obj, get_pos(so_obj))'] +% 'point_vals(so_obj, get_pos(so_obj))'] % - printstr - string for printing slice overlay figure window, e.g. % 'print -dpsc -painters -noui' (the default) % - printfile - name of file to print output to; default 'slices.ps' @@ -138,100 +138,102 @@ % in matrices as above % % FORMAT vol = slover('matrix2vol', mat3d, mat) -% returns (pseudo) vol struct for 3d matrix +% returns (pseudo) vol struct for 3d matrix % input matrices as above % % FORMAT obj = slover('basic_ui' [,dispf]) % Runs basic UI to fetch some parameters, does display, returns object % If optional dispf parameter = 0, supresses display -% -% $Id: slover.m,v 1.2 2005/05/06 22:59:56 matthewbrett Exp $ - +%__________________________________________________________________________ + +% Matthew Brett +% $Id: slover.m 6623 2015-12-03 18:38:08Z guillaume $ + myclass = 'slover'; % Default object structure defstruct = struct('img', [], ... - 'transform', 'axial', ... - 'slicedef', [], ... - 'slices', [], ... - 'figure', [], ... - 'figure_struct', [], ... - 'refreshf', 1, ... - 'clf', 1, ... - 'resurrectf', 1, ... - 'userdata', 1, ... - 'area', [], ... - 'xslices', [], ... - 'cbar', [], ... - 'labels', [], ... - 'callback', ';', ... - 'printstr', 'print -dpsc -painters -noui', ... - 'printfile', 'slices.ps'); + 'transform', 'axial', ... + 'slicedef', [], ... + 'slices', [], ... + 'figure', [], ... + 'figure_struct', [], ... + 'refreshf', 1, ... + 'clf', 1, ... + 'resurrectf', 1, ... + 'userdata', 1, ... + 'area', [], ... + 'xslices', [], ... + 'cbar', [], ... + 'labels', [], ... + 'callback', ';', ... + 'printstr', 'print -dpsc -painters -noui', ... + 'printfile', 'slices.ps'); if nargin < 1 - o = class(defstruct, myclass); - others = []; - return + o = class(defstruct, myclass); + others = []; + return end if nargin < 2 - others = []; + others = []; end % parse out string action calls (class functions) if ischar(params) - switch params - case 'getcmap' - if nargin < 2 - error('Need colormap name'); + switch params + case 'getcmap' + if nargin < 2 + error('Need colormap name'); + end + o = pr_getcmap(others); + return + case 'volmaxmin' + if nargin < 2 + error('Need volume to calculate max/min'); + end + [o,others] = pr_volmaxmin(others); + return + case 'blobs2vol' + if nargin < 4 + error('Need XYZ, vals, mat'); + end + o = pr_blobs2vol(others, varargin{:}); + return + case 'matrix2vol' + if nargin < 3 + error('Need matrix and mat'); + end + o = pr_matrix2vol(others, varargin{:}); + return + case 'basic_ui' + o = pr_basic_ui(others, varargin{:}); + if ~isempty(o), o = paint(o); end + return end - o = pr_getcmap(others); - return - case 'volmaxmin' - if nargin < 2 - error('Need volume to calculate max/min'); - end - [o others] = pr_volmaxmin(others); - return - case 'blobs2vol' - if nargin < 4 - error('Need XYZ, vals, mat'); - end - o = pr_blobs2vol(others, varargin{:}); - return - case 'matrix2vol' - if nargin < 3 - error('Need matrix and mat'); - end - o = pr_matrix2vol(others, varargin{:}); - return - case 'basic_ui' - o = pr_basic_ui(others, varargin{:}); - if ~isempty(o), o = paint(o); end - return - end - - % if not action string, must be filename(s) - params = spm_vol(params); -end + + % if not action string, must be filename(s) + params = spm_vol(params); +end % Could these just be image vol structs? if isfield(params, 'fname') - for i = 1:numel(params) - obj.img(i).vol = params(i); - end - params = obj; + for i = 1:numel(params) + obj.img(i).vol = params(i); + end + params = obj; end % Deal with passed objects of this (or child) class if isa(params, myclass) - o = params; - % Check for simple form of call - if isempty(others), return, end - - % Otherwise, we are being asked to set fields of object - [p others] = mars_struct('split', others, defstruct); - o = mars_struct('ffillmerge', o, p); - return + o = params; + % Check for simple form of call + if isempty(others), return, end + + % Otherwise, we are being asked to set fields of object + [p,others] = mars_struct('split', others, defstruct); + o = mars_struct('ffillmerge', o, p); + return end % fill params with defaults, parse into fields for this object, children @@ -243,5 +245,3 @@ % refill with defaults o = fill_defaults(o); - -return \ No newline at end of file diff --git a/@slover/subsasgn.m b/@slover/subsasgn.m index 77109d18..2c933fdb 100644 --- a/@slover/subsasgn.m +++ b/@slover/subsasgn.m @@ -1,7 +1,9 @@ function result = subsasgn(this, Struct, rhs) -% method to overload . notation in assignments. +% Method to overload . notation in assignments. % . assignment works directly on object fields -% -% $Id: subsasgn.m,v 1.1 2005/04/20 15:05:36 matthewbrett Exp $ +%__________________________________________________________________________ + +% Matthew Brett +% $Id: subsasgn.m 6623 2015-12-03 18:38:08Z guillaume $ result = builtin('subsasgn', this, Struct, rhs); diff --git a/@slover/subsref.m b/@slover/subsref.m index 40a8fcbd..d3471363 100644 --- a/@slover/subsref.m +++ b/@slover/subsref.m @@ -1,7 +1,9 @@ function result = subsref(this, Struct) -% method to overload the . notation. +% Method to overload the . notation. % . reference works directly on object fields -% -% $Id: subsref.m,v 1.1 2005/04/20 15:05:36 matthewbrett Exp $ +%__________________________________________________________________________ -result = builtin('subsref', this, Struct ); \ No newline at end of file +% Matthew Brett +% $Id: subsref.m 6623 2015-12-03 18:38:08Z guillaume $ + +result = builtin('subsref', this, Struct ); diff --git a/@xmltree/Contents.m b/@xmltree/Contents.m index 15f950ad..5caf714c 100644 --- a/@xmltree/Contents.m +++ b/@xmltree/Contents.m @@ -1,5 +1,5 @@ -% XMLTree: XML Toolbox for MATLAB. -% Version 1.3 21-Apr-2008 +% XMLTree: XML Toolbox for MATLAB and GNU Octave +% Version 1.4 13-Jun-2015 % % XML file I/O. % xmltree - Constructor (XML parser). @@ -23,22 +23,21 @@ % parent - Return parents of a node. % root - Return the root element of a tree. % set - Set node properties. -% setfilename - Set filename +% setfilename - Set filename. % -% Graphical user interface methods (work in progress). -% editor - Reimplementation of for MATLAB 6+ -% view - Graphical display of a tree. -% view_ui - Useful function for view method. +% Graphical user interface methods (basic). +% editor - Graphical display of a tree. +% view - (deprecated). % % Low level class methods. % char - Convert a tree into a string (for display). -% display - Display a tree into MATLAB. +% display - Display a tree in the workspace. % % Private methods. % xml_parser - XML parser. -% xml_findstr - Find one string within another (mexfile) +% xml_findstr - Find one string within another (C-MEX file). % -% Conversions MATLAB <=> XML +% Conversions struct <=> XML. % loadxml - % savexml - % mat2xml - @@ -50,7 +49,7 @@ % xmldemo2 - Read an XML file and access fields. % xmldemo3 - Read an XML file, modify some fields and save it. -% Copyright 2002-2011 http://www.artefact.tk/ +% Copyright 2002-2015 http://www.artefact.tk/ % Guillaume Flandin -% $Id: Contents.m 4460 2011-09-05 14:52:16Z guillaume $ +% $Id: Contents.m 6480 2015-06-13 01:08:30Z guillaume $ diff --git a/@xmltree/convert.m b/@xmltree/convert.m index 7a783238..662a7018 100644 --- a/@xmltree/convert.m +++ b/@xmltree/convert.m @@ -1,5 +1,5 @@ function s = convert(tree,uid) -% XMLTREE/CONVERT Converter an XML tree in a Matlab structure +% XMLTREE/CONVERT Converter an XML tree in a structure % % tree - XMLTree object % uid - uid of the root of the subtree, if provided. @@ -7,21 +7,21 @@ % s - converted structure %__________________________________________________________________________ % -% Convert an xmltree into a Matlab structure, when possible. +% Convert an XMLTree into a structure, when possible. % When several identical tags are present, a cell array is used. % The root tag is not saved in the structure. % If provided, only the structure corresponding to the subtree defined % by the uid UID is returned. %__________________________________________________________________________ -% Copyright (C) 2002-2011 http://www.artefact.tk/ +% Copyright (C) 2002-2015 http://www.artefact.tk/ % Guillaume Flandin -% $Id: convert.m 4460 2011-09-05 14:52:16Z guillaume $ +% $Id: convert.m 6480 2015-06-13 01:08:30Z guillaume $ % Exemple: -% tree: field1field2field3 -% toto = convert(tree); -% <=> toto = struct('titi',{{'field1', 'field3'}},'tutu','field2') +% tree = 'field1field2field3'; +% toto = convert(xmltree(tree)); +% <=> toto = struct('b',{{'field1', 'field3'}},'c','field2') %error(nargchk(1,2,nargin)); @@ -86,7 +86,7 @@ % end %- case 'chardata' s = sub_setfield(s,arg{:},get(tree,uid,'value')); - %- convert strings into their Matlab equivalent when possible + %- convert strings into their numerical equivalent when possible %- e.g. string '3.14159' becomes double scalar 3.14159 % v = get(tree,uid,'value'); %- % cv = str2num(v); %- @@ -113,7 +113,7 @@ try s = sub_setfield(s,arg{:},feval(app,get(tree,uid,'value'))); catch - warning('[XMLTREE] Unknown target application'); + warning('[XMLTree] Unknown target application'); end end case 'comment' diff --git a/@xmltree/copy.m b/@xmltree/copy.m index d61cb385..9f9a1d91 100644 --- a/@xmltree/copy.m +++ b/@xmltree/copy.m @@ -7,13 +7,13 @@ % uid - UID of the element where the subtree must be duplicated %__________________________________________________________________________ % -% Copy a subtree to another branch -% The tree parameter must be in input AND in output +% Copy a subtree to another branch. +% The tree parameter must be in input AND in output. %__________________________________________________________________________ -% Copyright (C) 2002-2011 http://www.artefact.tk/ +% Copyright (C) 2002-2015 http://www.artefact.tk/ % Guillaume Flandin -% $Id: copy.m 4460 2011-09-05 14:52:16Z guillaume $ +% $Id: copy.m 6480 2015-06-13 01:08:30Z guillaume $ %error(nargchk(2,3,nargin)); diff --git a/@xmltree/editor.m b/@xmltree/editor.m index dccb6bca..0c82a0f4 100644 --- a/@xmltree/editor.m +++ b/@xmltree/editor.m @@ -1,17 +1,16 @@ function editor(tree) %XMLTREE/EDITOR A Graphical User Interface for an XML tree -% EDITOR(TREE) opens a new Matlab figure displaying the xmltree -% object TREE. +% EDITOR(TREE) opens a new figure displaying the xmltree object TREE. % H = EDITOR(TREE) also returns the figure handle H. % % This is a beta version of successor % % See also XMLTREE %__________________________________________________________________________ -% Copyright (C) 2002-2011 http://www.artefact.tk/ +% Copyright (C) 2002-2015 http://www.artefact.tk/ % Guillaume Flandin -% $Id: editor.m 4460 2011-09-05 14:52:16Z guillaume $ +% $Id: editor.m 6524 2015-08-18 10:09:01Z guillaume $ %error(nargchk(1,1,nargin)); @@ -71,7 +70,7 @@ function editor(tree) 'Tag', 'xmllistbox'); %- Right box -uicontrol('Style', 'list', ... +uicontrol('Style', 'listbox', ... 'HorizontalAlignment','left', ... 'Units','Normalized', ... 'Visible','on',... @@ -202,7 +201,7 @@ function doModify(fig,evd,h) pos = get(handles.xmllistbox,'value'); uid = uidList(pos); contents = children(tree,uid); - if length(contents) > 0 & ... + if length(contents) > 0 && ... strcmp(get(tree,contents(1),'type'),'chardata') str = get(tree,contents(1),'value'); prompt = {'Name :','New value:'}; @@ -316,7 +315,7 @@ function doList(fig,evd,h) %- Single mouse click if strcmp(get(h,'SelectionType'),'normal') contents = children(tree, uid); - if length(contents) > 0 & ... + if length(contents) > 0 && ... strcmp(get(tree,contents(1),'type'),'chardata') str = get(tree,contents(1),'value'); set(handles.addbutton,'Enable','off'); @@ -360,7 +359,7 @@ function doUpdate(fig,evd,h) function [batchString, uidList] = doUpdateR(tree, uid, o) if nargin < 2, uid = root(tree); end - if nargin < 3 | o == 0 + if nargin < 3 || o == 0 o = 0; sep = ' '; else @@ -374,7 +373,7 @@ function doUpdate(fig,evd,h) uidList = [get(tree,uid,'uid')]; haselementchild = 0; contents = get(tree, uid, 'contents'); - if isfield(tree, uid, 'show') & get(tree, uid, 'show') == 1 + if isfield(tree, uid, 'show') && get(tree, uid, 'show') == 1 for i=1:length(contents) if strcmp(get(tree,contents(i),'type'),'element') [subbatchString, subuidList] = doUpdateR(tree,contents(i),o+1); diff --git a/@xmltree/move.m b/@xmltree/move.m index 1afe353b..a9613463 100644 --- a/@xmltree/move.m +++ b/@xmltree/move.m @@ -7,12 +7,12 @@ %__________________________________________________________________________ % % Move a subtree inside a tree from A to B. -% The tree parameter must be in input AND in output +% The tree parameter must be in input AND in output. %__________________________________________________________________________ -% Copyright (C) 2002-2011 http://www.artefact.tk/ +% Copyright (C) 2002-2015 http://www.artefact.tk/ % Guillaume Flandin -% $Id: move.m 4460 2011-09-05 14:52:16Z guillaume $ +% $Id: move.m 6480 2015-06-13 01:08:30Z guillaume $ %error(nargchk(3,3,nargin)); diff --git a/@xmltree/private/xml_findstr.c b/@xmltree/private/xml_findstr.c index 7a19e57e..1d5a0cf1 100644 --- a/@xmltree/private/xml_findstr.c +++ b/@xmltree/private/xml_findstr.c @@ -1,12 +1,12 @@ #include "mex.h" /* - * $Id: xml_findstr.c 4151 2011-01-07 18:09:25Z guillaume $ + * $Id: xml_findstr.c 6480 2015-06-13 01:08:30Z guillaume $ * Guillaume Flandin */ /* - Differences with matlab built-in findstr: + Differences with built-in findstr: - allows to search only the n first occurences of a pattern - allows to search only in a substring (given an index of the beginning) @@ -14,8 +14,8 @@ - doesn't use mxGetString to prevent a copy of the string. - assumes MATLAB stores strings as unsigned short (Unicode 16 bits) matrix.h: typedef uint16_T mxChar; - (that's the case for MATLAB 5.*, 6.* and 7.* but MATLAB 4.* stores strings - as double and Octave as char, see src/mxarray.h) + (that's the case for MATLAB 5.*, 6.* and 7.* but MATLAB 4.* stores + strings as double and GNU Octave as char, see src/mxarray.h) */ /* Comment the following line to use standard mxGetString (slower) */ diff --git a/@xmltree/private/xml_findstr.mexw32 b/@xmltree/private/xml_findstr.mexw32 index 3a886c13..724484ef 100755 Binary files a/@xmltree/private/xml_findstr.mexw32 and b/@xmltree/private/xml_findstr.mexw32 differ diff --git a/@xmltree/private/xml_findstr.mexw64 b/@xmltree/private/xml_findstr.mexw64 index 14f48248..b2baa9e0 100755 Binary files a/@xmltree/private/xml_findstr.mexw64 and b/@xmltree/private/xml_findstr.mexw64 differ diff --git a/@xmltree/private/xml_parser.m b/@xmltree/private/xml_parser.m index 5b31078c..61cf2b65 100644 --- a/@xmltree/private/xml_parser.m +++ b/@xmltree/private/xml_parser.m @@ -6,20 +6,20 @@ % tree - tree structure corresponding to the XML file %__________________________________________________________________________ % -% xml_parser.m is an XML 1.0 (http://www.w3.org/TR/REC-xml) parser -% written in Matlab. It aims to be fully conforming. It is currently not -% a validating XML processor. +% xml_parser.m is an XML 1.0 (http://www.w3.org/TR/REC-xml) parser. +% It aims to be fully conforming. It is currently not a validating +% XML processor. % % A description of the tree structure provided in output is detailed in % the header of this m-file. %__________________________________________________________________________ -% Copyright (C) 2002-2011 http://www.artefact.tk/ +% Copyright (C) 2002-2015 http://www.artefact.tk/ % Guillaume Flandin -% $Id: xml_parser.m 4460 2011-09-05 14:52:16Z guillaume $ +% $Id: xml_parser.m 6480 2015-06-13 01:08:30Z guillaume $ -% XML Processor for MATLAB (The Mathworks, Inc.). -% Copyright (C) 2002-2011 Guillaume Flandin +% XML Processor for GNU Octave and MATLAB (The Mathworks, Inc.) +% Copyright (C) 2002-2015 Guillaume Flandin % % This program is free software; you can redistribute it and/or % modify it under the terms of the GNU General Public License @@ -44,14 +44,14 @@ %-------------------------------------------------------------------------- % The implementation of this XML parser is much inspired from a -% Javascript parser available at +% Javascript parser that used to be available at -% A mex-file xml_findstr.c is also required, to encompass some -% limitations of the built-in findstr Matlab function. +% A C-MEX file xml_findstr.c is also required, to encompass some +% limitations of the built-in FINDSTR function. % Compile it on your architecture using 'mex -O xml_findstr.c' command % if the compiled version for your system is not provided. -% If this function behaves badly (crash or wrong results), comment the -% line '#define __HACK_MXCHAR__' in xml_findstr.c and compile it again. +% If this function does not behave as expected, comment the line +% '#define __HACK_MXCHAR__' in xml_findstr.c and compile it again. %-------------------------------------------------------------------------- % Structure of the output tree: @@ -155,6 +155,7 @@ if isempty(TagStart) %- Character data error('[XML] Unknown data at the end of the XML file.'); + Xparse_count = Xparse_count + 1; xtree{Xparse_count} = chardata; xtree{Xparse_count}.value = erode(entity(xmlstring(frag.str:end))); xtree{Xparse_count}.parent = frag.parent; @@ -166,6 +167,7 @@ frag.str = TagStart; else %- Character data + Xparse_count = Xparse_count + 1; xtree{Xparse_count} = chardata; xtree{Xparse_count}.value = erode(entity(xmlstring(frag.str:TagStart-1))); xtree{Xparse_count}.parent = frag.parent; @@ -224,6 +226,7 @@ name = starttag(1:nextspace-1); attribs = starttag(nextspace+1:end); end + Xparse_count = Xparse_count + 1; xtree{Xparse_count} = element; xtree{Xparse_count}.name = strip(name); if frag.parent @@ -253,6 +256,7 @@ warning('[XML] Tag close || nextspace == frag.str+2 xtree{Xparse_count}.value = erode(xmlstring(frag.str+2:close-1)); @@ -274,6 +278,7 @@ if isempty(close) warning('[XML] Tag r%s %s\n', ... - locRevs{locInd}, remRevs{inds(k)}, remFiles{inds(k)}); - end - end - - if any(remoteDeleted) - fprintf('\nthe following files are no longer part of FieldTrip:\n'); - files = locFiles(remoteDeleted); - for k = 1:numel(files) - fprintf(' %s\n', files{k}); - end - end - - if any(remoteNew) - fprintf('\nthe following new files are available:\n'); - files = remFiles(remoteNew); - revs = remRevs(remoteNew); - for k = 1:numel(files) - fprintf(' r%sa %s\n', revs{k}, files{k}); - end - end - - fprintf('\n'); - - end - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - case 'update' - % get the remote changes, check that there are no conflicting local - % changes and update the files - - if issvn - - fprintf('\nThis is an SVN working copy of FieldTrip, invoking ''svn update''...\n'); - olddir = pwd(); - cd(ftpath); - system('svn update'); - cd(olddir); - fprintf('\n'); - - else - - % fetch remote hash table - str = urlread(remotesignature); - [remHashes, remFiles, remRevs] = parseHashTable(str); - - % fetch local hash table - str = fileread(signaturefile); - [locHashes, locFiles, locRevs] = parseHashTable(str); - - % determine changes - [remoteDeleted, remoteNew, remoteChanges, localDeleted, localChanges] = findChanges(locHashes, locFiles, remHashes, remFiles, ftpath); - - % index all possible deletions and additions - locFilesDelete = []; - remFilesDownload = []; - - olddir = pwd(); - cd(ftpath); - - % keep track of whether local additions will possibly be overwritten - overwriteFlag = 0; - - % first display information on what would be changed - if any(remoteNew) - fprintf('\nthe following new files will be added:\n'); - inds = find(remoteNew); - for k = 1:numel(inds) - if exist(remFiles{inds(k)}, 'file') - msg = '!'; - overwriteFlag = 1; - else - msg = ' '; - end - - % give this change a number and remember it - remFilesDownload(end+1) = inds(k); - locFilesDelete (end+1) = nan; - - fprintf('%s %3d. r%s %s\n', msg, numel(locFilesDelete), remRevs{inds(k)}, remFiles{inds(k)}); - end - end - - if any(remoteDeleted) - fprintf('\nthe following files will be deleted:\n'); - inds = find(remoteDeleted); - for k = 1:numel(inds) - if localChanges(inds(k)) - msg = '!'; - overwriteFlag = 1; - else - msg = ' '; - end - - % give this change a number and remember it - remFilesDownload(end+1) = nan; - locFilesDelete (end+1) = inds(k); - - fprintf('%s %3d. %s\n', msg, numel(locFilesDelete), locFiles{inds(k)}); - end - end - - if any(remoteChanges) - fprintf('\nthe following files will be updated:\n'); - inds = find(remoteChanges); - for k = 1:numel(inds) - locInd = strcmp(locFiles, remFiles{inds(k)}); - if localChanges(locInd) - msg = '!'; - overwriteFlag = 1; - else - msg = ' '; - end - - % give this change a number and remember it - remFilesDownload(end+1) = inds(k); - locFilesDelete (end+1) = nan; - - fprintf('%s %3d. r%s-->r%s %s\n', msg, numel(locFilesDelete), locRevs{locInd}, remRevs{inds(k)}, remFiles{inds(k)}); - end - end - - assert(numel(remFilesDownload) == numel(locFilesDelete)); - - if overwriteFlag - fprintf(['\n!!NOTE: if you choose to include changes marked with a leading !, \n'... - ' they will overwrite local changes or additions!\n']); - end - - % prompt the user which changes should be incorporated - while (true) % use while (true) as poor man's do{}while() - changesToInclude = input(['\nwhich changes do you want to include?\n'... - '([vector] for specific changes, ''all'', or [] for none)\n? '], 's'); - - % check integrity of entered data - if strcmp(changesToInclude, 'all') - changesToInclude = 1:numel(remFilesDownload); - else - [changesToInclude, status] = str2num(changesToInclude); - end - - if status && isnumeric(changesToInclude) && all(changesToInclude > 0) && (all(changesToInclude <= numel(remFilesDownload)) || changesToInclude == Inf) - changesToInclude = unique(changesToInclude); - break; - end - end - - % include only those changes requested in the actual update - locFilesDelete = locFilesDelete(changesToInclude); - locFilesDelete(isnan(locFilesDelete)) = []; - remFilesDownload = remFilesDownload(changesToInclude); - remFilesDownload(isnan(remFilesDownload)) = []; - - % delete all local files that should be deleted - for k = 1:numel(locFilesDelete) - ind = locFilesDelete(k); - fprintf('deleting file %s...\n', locFiles{ind}); - delete(locFiles{ind}); - % also remove entry from the hash table cell arrays - locFiles(ind) = []; - locHashes(ind) = []; - locRevs(ind) = []; - end - - % add new files and/or update changed files - for k = 1:numel(remFilesDownload) - ind = remFilesDownload(k); - fprintf('downloading file %s...\n', remFiles{ind}); - newFile = urlread([repository remFiles{ind}]); - fp = fopen(remFiles{ind}, 'w'); - fwrite(fp, newFile); - fclose(fp); - - % update local hash table - locInd = strcmp(locFiles, remFiles{ind}); - if any(locInd) - % file was already present, update hash - locHashes{locInd} = remHashes{ind}; - locRevs{locInd} = remRevs{ind}; - else - % new file, add hash - locFiles{end} = remFiles{ind}; - locHashes{end} = remHashes{ind}; - locRevs{end} = remRevs{ind}; - end - - end - - cd(olddir); - - if ~isempty(changesToInclude) - % output new hash table - fp = fopen(signaturefile, 'w'); - outputHashTable(fp, locHashes, locFiles, locRevs); - fclose(fp); - - fprintf('update finished.\n'); - else - fprintf('nothing updated.\n'); - end - - ft_version(); - - end - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - case 'signature' - - if exist(signaturefile, 'file') - error('%s already exists.\nIf you are sure you want to re-create the signature file, you will have to manually delete it.\nNote that this means that local edits cannot be tracked anymore, and might be overwritten without notice!', signaturefile); - end - - fprintf('\nwriting signature file to signature-local.md5 (this might take a few minutes)...'); - hashFile = fopen(signaturefile, 'w'); - hashAll(hashFile, ftpath, ftpath); - fclose(hashFile); - fprintf('done.\n\n'); - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - case 'fullsignature' - - if exist(signaturefile, 'file') - error('%s already exists.\nIf you are sure you want to re-create the signature file, you will have to manually delete it.\nNote that this means that local edits cannot be tracked anymore, and might be overwritten without notice!', signaturefile); - end - - if ~issvn - error('you can only generate a full signature with revision info if you are using an SVN working copy of FieldTrip'); - end - - fprintf('\nwriting signature file to signature-local.md5 (this might take a few minutes)...'); - hashFile = fopen(fullfile(ftpath, 'signature-local.md5'), 'w'); - hashAll(hashFile, ftpath, ftpath, 1); - fclose(hashFile); - fprintf('done.\n\n'); - -end % switch cmd - -end % function ft_version - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -function [remoteDeleted, remoteNew, remoteChanges, localDeleted, localChanges] = findChanges(locHashes, locFiles, remHashes, remFiles, ftpath) -% locHashes and locFiles represent the hashes and filenames of entries in -% the local hash table, respectively. remHashes and remFiles represent the -% hashes and filenames of entries in the remote hash table, respectively. -% ftpath is the FT root path. Returned are logical index vectors into the -% remFiles cell array (for remoteChanges and remoteNew) or into the -% locFiles cell array (rest). - -fprintf('\ncomparing local and remote (latest) hashes, this might take a minute...\n'); - -remoteDeleted = false(size(locFiles)); -localChanges = false(size(locFiles)); -remoteChanges = false(size(remFiles)); -localDeleted = false(size(locFiles)); -% note: no keeping track of local additions (not desirable probably) - -% compare them by looping over all files in local table -for k = 1:numel(locFiles) - ind = strcmp(remFiles, locFiles{k}); - - if sum(ind) > 1 - % this should never happen; means remote table is corrupt - warning('more than one entry found in remote hash table for file %s', locFiles{k}); - end - - if ~any(ind) - % file not found in remote table, should be deleted locally - remoteDeleted(k) = 1; - continue; - end - - if ~strcmp(remHashes{ind}, locHashes{k}) - % hash remote different from hash in local table - remoteChanges(ind) = 1; - end - - if ~exist(fullfile(ftpath, locFiles{k}), 'file') - localDeleted(k) = 1; - continue; - end - - if (nargout > 4) % only check for local changes if requested, slow step - if ~strcmp(CalcMD5(fullfile(ftpath, locFiles{k}), 'File'), locHashes{k}) - % hash of actual file different from hash in local table - localChanges(k) = 1; - end - end -end - -% only remote additions have not yet been determined, do so now -remoteNew = false(size(remFiles)); -[dummy, inds] = setdiff(remFiles, locFiles); -remoteNew(inds) = 1; - -end % function findChanges - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -function [hashes, files, revs] = parseHashTable(str) -% If str is a tab-separated string, with records separated by newlines, -% parseHashTable(str) will return the three columns in the file: the first -% column will be returned as hashes; the second as revs, the third as -% files. - -lines = regexp(str, '\n', 'split'); -if isempty(lines{end}) - lines = lines(1:end-1); +if isempty(isgit) + % are we dealing with an GIT working copy of fieldtrip? + isgit = isdir(fullfile(ftpath, '.git')); end -hashes = cell(1, numel(lines)); -files = cell(1, numel(lines)); -revs = cell(1, numel(lines)); - -for k = 1:numel(lines) - tmp = regexp(lines{k}, '\t', 'split'); - hashes{k} = tmp{1}; - revs{k} = tmp{2}; - files{k} = tmp{3}; -end - -end % function parseHashTable - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -function hashAll(filePointer, path, basepath, withRev) - -if nargin < 4 - withRev = 0; -end - -% these files are always excluded from the update and hashing routines -file_excludes = {'.', '..', '.svn', 'test', '.DS_Store', 'signature.md5', 'signature-local.md5'}; - -list = dir(path); -for k = 1:numel(list) - if ~any(strcmp(list(k).name, file_excludes)) - filename = [path '/' list(k).name]; - if (list(k).isdir) - hashAll(filePointer, filename, basepath, withRev); - else - md5 = CalcMD5(filename, 'File'); - - if withRev - [status, output] = system(['svn info ' filename]); - rev = regexp(output, 'Last Changed Rev: (.*)', 'tokens', 'dotexceptnewline'); - if ~isempty(rev) && ~isempty(rev{1}) - rev = rev{1}{1}; - else - rev = 'UNKNOWN'; - end - else - rev = 'UNKNOWN'; - end - filename = filename((numel(basepath)+2):end); % strip off the base path - fprintf(filePointer, '%s\t%s\t%s\n', md5, rev, filename); +% show the latest revision present in this copy of fieldtrip + +if issvn + % use svn system call to determine latest revision + olddir = pwd(); + cd(ftpath); + [status, output] = system('svn info'); + cd(olddir); + if status > 0 + if ~ispc + % the command line tools will probably not be available on windows + warning('you seem to have an SVN development copy of FieldTrip, yet ''svn info'' does not work as expected'); end + ftver = 'unknown'; + else + rev = regexp(output, 'Revision: (.*)', 'tokens', 'dotexceptnewline'); + rev = rev{1}{1}; + ftver = ['r' rev]; end -end -end % function hashAll +elseif isgit + tmpfile = tempname; -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + olddir = pwd(); + cd(ftpath); + [status, output] = system(sprintf('git show > %s', tmpfile)); + cd(olddir); + if status > 0 + % FIXME the command line tools will probably not be available on windows + error('you seem to have an GIT development copy of FieldTrip, yet ''git show'' does not work as expected'); + end -function outputHashTable(filePointer, hashes, files, revs) + fp = fopen(tmpfile); + if fp>0 + line = fgetl(fp); % the first line contains the commit number + fclose(fp); + rev = regexp(line, ' ', 'split'); + rev = rev{2}; + + % this is a string like 4d3c309129f12146885120c2853a11362e048ea7 + ftver = rev; + else + ftver = 'unknown'; + end -% sort the entries (highest revs should always be at the top) -numRevs = cellfun(@str2double, revs); -[sortedRevs, inds] = sort(numRevs, 'descend'); -lastnan = find(isnan(sortedRevs), 1, 'last'); % put nans (=UNKNOWN) at the end -inds = [inds(lastnan+1:end) inds(1:lastnan)]; +else + % get it from the Contents.m file in the FieldTrip release + a = ver(ftpath); + ftver = a.Version; -hashes = hashes(inds); -files = files(inds); -revs = revs(inds); +end % if issvn, isgit or otherwise -for k = 1:numel(hashes) - fprintf(filePointer, '%s\t%s\t%s\n', hashes{k}, revs{k}, files{k}); +if nargout==0 + fprintf('\nThis is FieldTrip, version %s.\n\n', ftver); + clear ftver end - -end % function outputHashTable diff --git a/external/fieldtrip/utilities/ft_warning.m b/external/fieldtrip/utilities/ft_warning.m new file mode 100644 index 00000000..03286e95 --- /dev/null +++ b/external/fieldtrip/utilities/ft_warning.m @@ -0,0 +1,258 @@ +function [ws, warned] = ft_warning(varargin) + +% FT_WARNING will throw a warning for every unique point in the +% stacktrace only, e.g. in a for-loop a warning is thrown only once. +% +% Use as one of the following +% ft_warning(string) +% ft_warning(id, string) +% Alternatively, you can use ft_warning using a timeout +% ft_warning(string, timeout) +% ft_warning(id, string, timeout) +% where timeout should be inf if you don't want to see the warning ever +% again. +% +% Use as ft_warning('-clear') to clear old warnings from the current +% stack +% +% It can be used instead of the MATLAB built-in function WARNING, thus as +% s = ft_warning(...) +% or as +% ft_warning(s) +% where s is a structure with fields 'identifier' and 'state', storing the +% state information. In other words, ft_warning accepts as an input the +% same structure it returns as an output. This returns or restores the +% states of warnings to their previous values. +% +% It can also be used as +% [s w] = ft_warning(...) +% where w is a boolean that indicates whether a warning as been thrown or not. +% +% Please note that you can NOT use it like this +% ft_warning('the value is %d', 10) +% instead you should do +% ft_warning(sprintf('the value is %d', 10)) + +% Copyright (C) 2012, Robert Oostenveld +% Copyright (C) 2013, Robert Oostenveld, J?rn M. Horschig +% +% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id: ft_warning.m 10531 2015-07-13 14:11:06Z roboos $ + +global ft_default + +if nargin < 1 + error('You need to specify at least a warning message'); +end + +warned = false; +if isstruct(varargin{1}) + warning(varargin{1}); + return; +end + +if ~isfield(ft_default, 'warning') + ft_default.warning = []; +end +if ~isfield(ft_default.warning, 'stopwatch') + ft_default.warning.stopwatch = []; +end +if ~isfield(ft_default.warning, 'identifier') + ft_default.warning.identifier = []; +end +if ~isfield(ft_default.warning, 'ignore') + ft_default.warning.ignore = {}; +end + +% put the arguments we will pass to warning() in this cell array +warningArgs = {}; + +if nargin==3 + % calling syntax (id, msg, timeout) + + warningArgs = varargin(1:2); + msg = warningArgs{2}; + timeout = varargin{3}; + fname = [warningArgs{1} '_' warningArgs{2}]; + +elseif nargin==2 && isnumeric(varargin{2}) + % calling syntax (msg, timeout) + + warningArgs = varargin(1); + msg = warningArgs{1}; + timeout = varargin{2}; + fname = warningArgs{1}; + +elseif nargin==2 && isequal(varargin{1}, 'off') + + ft_default.warning.ignore = union(ft_default.warning.ignore, varargin{2}); + return + +elseif nargin==2 && isequal(varargin{1}, 'on') + + ft_default.warning.ignore = setdiff(ft_default.warning.ignore, varargin{2}); + return + +elseif nargin==2 && ~isnumeric(varargin{2}) + % calling syntax (id, msg) + + warningArgs = varargin(1:2); + msg = warningArgs{2}; + timeout = inf; + fname = [warningArgs{1} '_' warningArgs{2}]; + +elseif nargin==1 + % calling syntax (msg) + + warningArgs = varargin(1); + msg = warningArgs{1}; + timeout = inf; % default timeout in seconds + fname = [warningArgs{1}]; + +end + +if ismember(msg, ft_default.warning.ignore) + % do not show this warning + return; +end + +if isempty(timeout) + error('Timeout ill-specified'); +end + +if timeout ~= inf + fname = decomma(fixname(fname)); % make a nice string that is allowed as structure fieldname + if length(fname) > 63 % MATLAB max name + fname = fname(1:63); + end + line = []; +else + % here, we create the fieldname functionA.functionB.functionC... + [tmpfname ft_default.warning.identifier line] = fieldnameFromStack(ft_default.warning.identifier); + if ~isempty(tmpfname) + fname = tmpfname; + clear tmpfname; + end +end + +if nargin==1 && ischar(varargin{1}) && strcmp('-clear', varargin{1}) + if strcmp(fname, '-clear') % reset all fields if called outside a function + ft_default.warning.identifier = []; + ft_default.warning.stopwatch = []; + else + if issubfield(ft_default.warning.identifier, fname) + ft_default.warning.identifier = rmsubfield(ft_default.warning.identifier, fname); + end + end + return; +end + +% and add the line number to make this unique for the last function +fname = horzcat(fname, line); + +if ~issubfield('ft_default.warning.stopwatch', fname) + ft_default.warning.stopwatch = setsubfield(ft_default.warning.stopwatch, fname, tic); +end + +now = toc(getsubfield(ft_default.warning.stopwatch, fname)); % measure time since first function call + +if ~issubfield(ft_default.warning.identifier, fname) || ... + (issubfield(ft_default.warning.identifier, fname) && now>getsubfield(ft_default.warning.identifier, [fname '.timeout'])) + + % create or reset field + ft_default.warning.identifier = setsubfield(ft_default.warning.identifier, fname, []); + + % warning never given before or timed out + ws = warning(warningArgs{:}); + ft_default.warning.identifier = setsubfield(ft_default.warning.identifier, [fname '.timeout'], now+timeout); + ft_default.warning.identifier = setsubfield(ft_default.warning.identifier, [fname '.ws'], msg); + warned = true; +else + + % the warning has been issued before, but has not timed out yet + ws = getsubfield(ft_default.warning.identifier, [fname '.ws']); + +end + +end % function ft_warning + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% helper functions +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function name = decomma(name) +name(name==',')=[]; +end % function + +function [fname ft_previous_warnings line] = fieldnameFromStack(ft_previous_warnings) +% stack(1) is this function, stack(2) is ft_warning +stack = dbstack('-completenames'); +if size(stack) < 3 + fname = []; + line = []; + return; +end +i0 = 3; +% ignore ft_preamble +while strfind(stack(i0).name, 'ft_preamble') + i0=i0+1; +end + +fname = horzcat(fixname(stack(end).name)); +if ~issubfield(ft_previous_warnings, fixname(stack(end).name)) + ft_previous_warnings.(fixname(stack(end).name)) = []; % iteratively build up structure fields +end + + +for i=numel(stack)-1:-1:(i0) + % skip postamble scripts + if strncmp(stack(i).name, 'ft_postamble', 12) + break; + end + + fname = horzcat(fname, '.', horzcat(fixname(stack(i).name))); % , stack(i).file + if ~issubfield(ft_previous_warnings, fname) % iteratively build up structure fields + setsubfield(ft_previous_warnings, fname, []); + end +end + +% line of last function call +line = ['.line', int2str(stack(i0).line)]; +end + +% function outcome = issubfield(strct, fname) +% substrindx = strfind(fname, '.'); +% if numel(substrindx) > 0 +% % separate the last fieldname from all former +% outcome = eval(['isfield(strct.' fname(1:substrindx(end)-1) ', ''' fname(substrindx(end)+1:end) ''')']); +% else +% % there is only one fieldname +% outcome = isfield(strct, fname); +% end +% end + +% function strct = rmsubfield(strct, fname) +% substrindx = strfind(fname, '.'); +% if numel(substrindx) > 0 +% % separate the last fieldname from all former +% strct = eval(['rmfield(strct.' fname(1:substrindx(end)-1) ', ''' fname(substrindx(end)+1:end) ''')']); +% else +% % there is only one fieldname +% strct = rmfield(strct, fname); +% end +% end diff --git a/external/fieldtrip/utilities/ft_warp_apply.m b/external/fieldtrip/utilities/ft_warp_apply.m index 2e60649e..88005c27 100644 --- a/external/fieldtrip/utilities/ft_warp_apply.m +++ b/external/fieldtrip/utilities/ft_warp_apply.m @@ -70,7 +70,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: ft_warp_apply.m 10131 2015-01-27 16:08:26Z johzum $ +% $Id: ft_warp_apply.m 11052 2016-01-09 17:51:12Z roboos $ if nargin<4 tol = []; @@ -163,13 +163,13 @@ M(3,1) M(3,2) 0 M(3,3) ]; end - + %warped = M * [input'; ones(1, size(input, 1))]; %warped = warped(1:3,:)'; - + % below achieves the same as lines 154-155 warped = [input ones(size(input, 1),1)]*M(1:3,:)'; - + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % using external function that returns a homogeneous transformation matrix %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/external/fieldtrip/utilities/ft_warp_error.m b/external/fieldtrip/utilities/ft_warp_error.m index 73949231..83648e2b 100644 --- a/external/fieldtrip/utilities/ft_warp_error.m +++ b/external/fieldtrip/utilities/ft_warp_error.m @@ -4,11 +4,11 @@ % and can be used as the goalfunction in a 3D warping minimalisation % % Use as -% [dist] = ft_warp_error(M, input, target, 'method') +% dist = ft_warp_error(M, input, target, 'method') % -% It returns the mean Euclidian distance (residu) when attempting to -% transform the input towards the target using transformation M -% and using the specified warping method. +% It returns the mean Euclidian distance (i.e. the residual) for an interative +% optimalization to transform the input towards the target using the +% transformation M with the specified warping method. % % See also FT_WARP_OPTIM, FT_WARP_APPLY @@ -44,7 +44,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: ft_warp_error.m 8756 2013-11-11 12:53:49Z roboos $ +% $Id: ft_warp_error.m 11052 2016-01-09 17:51:12Z roboos $ if ~isempty(M) % apply the warp to the input positions @@ -78,4 +78,3 @@ dif = input - target; dist = mean(sqrt(sum(dif' .^2))); end - diff --git a/external/fieldtrip/utilities/hasyokogawa.m b/external/fieldtrip/utilities/hasyokogawa.m index fa0c7516..09c0f321 100644 --- a/external/fieldtrip/utilities/hasyokogawa.m +++ b/external/fieldtrip/utilities/hasyokogawa.m @@ -5,7 +5,7 @@ % installed. Only the newest version of the toolbox is accepted. % % Use as -% [string] = hasyokogawa; +% string = hasyokogawa; % which returns a string describing the toolbox version, e.g. "12bitBeta3", % "16bitBeta3", or "16bitBeta6" for preliminary versions, or '1.4' for the % official Yokogawa MEG Reader Toolbox. An empty string is returned if the toolbox @@ -37,7 +37,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: hasyokogawa.m 10398 2015-05-08 15:56:53Z tilsan $ +% $Id: hasyokogawa.m 11052 2016-01-09 17:51:12Z roboos $ ws = warning('off', 'MATLAB:pfileOlderThanMfile'); @@ -54,7 +54,7 @@ elseif exist('GetMeg160ADbitInfoM') || exist('GetMeg160ChannelInfoM') || exist('GetMeg160ContinuousRawDataM') % start with unknown, try to refine the version version = 'unknown'; - + try % Call some functions with input argument "Inf": If % the functions are present they return their revision number. @@ -85,7 +85,7 @@ end end end - + else % return empty if none of them is present version = []; @@ -95,7 +95,7 @@ % return a true/false value if isempty(version) version = false; - else + else version = strcmpi(version, desired); end end diff --git a/external/fieldtrip/utilities/nearest.m b/external/fieldtrip/utilities/nearest.m index 534efe1a..0cffba6a 100644 --- a/external/fieldtrip/utilities/nearest.m +++ b/external/fieldtrip/utilities/nearest.m @@ -22,6 +22,8 @@ % return an error, but nearest(1:10, 0.99, true, true) will return 1. The % tolerance that is allowed is half the distance between the subsequent % values in the array. +% +% See also FIND % Copyright (C) 2002-2012, Robert Oostenveld % @@ -41,7 +43,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: nearest.m 10137 2015-01-28 11:19:58Z roboos $ +% $Id: nearest.m 11052 2016-01-09 17:51:12Z roboos $ mbreal(array); mbreal(val); @@ -122,11 +124,11 @@ % return the last occurence of the largest number [dum, indx] = max(flipud(array)); indx = numel(array) + 1 - indx; - + elseif val=val, 1, 'first'); if abs(array(indx2)-val) <= abs(array(indx3)-val) @@ -156,7 +158,7 @@ if ~wassorted indx = xidx(indx); end - + end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -182,4 +184,3 @@ function mbvector(a) if ndims(a) > 2 || (size(a, 1) > 1 && size(a, 2) > 1) error('Argument to mbvector must be a vector'); end - diff --git a/external/fieldtrip/utilities/printstruct.m b/external/fieldtrip/utilities/printstruct.m index b0382d30..7ad997b9 100644 --- a/external/fieldtrip/utilities/printstruct.m +++ b/external/fieldtrip/utilities/printstruct.m @@ -20,6 +20,8 @@ % s = printstruct(b) % % s = printstruct('c', randn(10)>0.5) +% +% See also DISP % Copyright (C) 2006-2013, Robert Oostenveld % @@ -39,7 +41,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: printstruct.m 10030 2014-12-09 15:22:51Z eelspa $ +% $Id: printstruct.m 11052 2016-01-09 17:51:12Z roboos $ if nargin==1 val = name; @@ -131,7 +133,7 @@ dum = [dum ' ' printval(val{i,j}) ',']; % add the element with a comma end dum = [dum ' ' printval(val{i,siz(2)})]; % add the last one without comma - + str = [str dum 10]; end str = sprintf('%s};\n', str); @@ -177,17 +179,17 @@ switch class(val) case 'char' str = ['''' val '''']; - + case {'single' 'double' 'int8' 'int16' 'int32' 'int64' 'uint8' 'uint16' 'uint32' 'uint64' 'logical'} str = printmat(val); - + case 'function_handle' str = ['@' func2str(val)]; - + case 'struct' warning('cannot print structure at this level'); str = '''FIXME: printing structures at this level is not supported'''; - + otherwise warning('cannot print unknown object at this level'); str = '''FIXME: printing unknown objects is not supported'''; diff --git a/external/fieldtrip/utilities/private/base64encode.m b/external/fieldtrip/utilities/private/base64encode.m new file mode 100644 index 00000000..026c0a92 --- /dev/null +++ b/external/fieldtrip/utilities/private/base64encode.m @@ -0,0 +1,159 @@ +function y = base64encode(x, eol) +%BASE64ENCODE Perform base64 encoding on a string. +% +% BASE64ENCODE(STR, EOL) encode the given string STR. EOL is the line ending +% sequence to use; it is optional and defaults to '\n' (ASCII decimal 10). +% The returned encoded string is broken into lines of no more than 76 +% characters each, and each line will end with EOL unless it is empty. Let +% EOL be empty if you do not want the encoded string broken into lines. +% +% STR and EOL don't have to be strings (i.e., char arrays). The only +% requirement is that they are vectors containing values in the range 0-255. +% +% This function may be used to encode strings into the Base64 encoding +% specified in RFC 2045 - MIME (Multipurpose Internet Mail Extensions). The +% Base64 encoding is designed to represent arbitrary sequences of octets in a +% form that need not be humanly readable. A 65-character subset +% ([A-Za-z0-9+/=]) of US-ASCII is used, enabling 6 bits to be represented per +% printable character. +% +% Examples +% -------- +% +% If you want to encode a large file, you should encode it in chunks that are +% a multiple of 57 bytes. This ensures that the base64 lines line up and +% that you do not end up with padding in the middle. 57 bytes of data fills +% one complete base64 line (76 == 57*4/3): +% +% If ifid and ofid are two file identifiers opened for reading and writing, +% respectively, then you can base64 encode the data with +% +% while ~feof(ifid) +% fwrite(ofid, base64encode(fread(ifid, 60*57))); +% end +% +% or, if you have enough memory, +% +% fwrite(ofid, base64encode(fread(ifid))); +% +% See also BASE64DECODE. + +% Author: Peter J. Acklam +% Time-stamp: 2004-02-03 21:36:56 +0100 +% E-mail: pjacklam@online.no +% URL: http://home.online.no/~pjacklam + + % check number of input arguments + error(nargchk(1, 2, nargin)); + + % make sure we have the EOL value + if nargin < 2 + eol = ''; %sprintf('\n'); + else + if sum(size(eol) > 1) > 1 + error('EOL must be a vector.'); + end + if any(eol(:) > 255) + error('EOL can not contain values larger than 255.'); + end + end + + if sum(size(x) > 1) > 1 + error('STR must be a vector.'); + end + + x = uint8(x); + eol = uint8(eol); + + ndbytes = length(x); % number of decoded bytes + nchunks = ceil(ndbytes / 3); % number of chunks/groups + nebytes = 4 * nchunks; % number of encoded bytes + + % add padding if necessary, to make the length of x a multiple of 3 + if rem(ndbytes, 3) + x(end+1 : 3*nchunks) = 0; + end + + x = reshape(x, [3, nchunks]); % reshape the data + y = repmat(uint8(0), 4, nchunks); % for the encoded data + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Split up every 3 bytes into 4 pieces + % + % aaaaaabb bbbbcccc ccdddddd + % + % to form + % + % 00aaaaaa 00bbbbbb 00cccccc 00dddddd + % + y(1,:) = bitshift(x(1,:), -2); % 6 highest bits of x(1,:) + + y(2,:) = bitshift(bitand(x(1,:), 3), 4); % 2 lowest bits of x(1,:) + y(2,:) = bitor(y(2,:), bitshift(x(2,:), -4)); % 4 highest bits of x(2,:) + + y(3,:) = bitshift(bitand(x(2,:), 15), 2); % 4 lowest bits of x(2,:) + y(3,:) = bitor(y(3,:), bitshift(x(3,:), -6)); % 2 highest bits of x(3,:) + + y(4,:) = bitand(x(3,:), 63); % 6 lowest bits of x(3,:) + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Now perform the following mapping + % + % 0 - 25 -> A-Z + % 26 - 51 -> a-z + % 52 - 61 -> 0-9 + % 62 -> + + % 63 -> / + % + % We could use a mapping vector like + % + % ['A':'Z', 'a':'z', '0':'9', '+/'] + % + % but that would require an index vector of class double. + % + z = repmat(uint8(0), size(y)); + i = y <= 25; z(i) = 'A' + double(y(i)); + i = 26 <= y & y <= 51; z(i) = 'a' - 26 + double(y(i)); + i = 52 <= y & y <= 61; z(i) = '0' - 52 + double(y(i)); + i = y == 62; z(i) = '+'; + i = y == 63; z(i) = '/'; + y = z; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Add padding if necessary. + % + npbytes = 3 * nchunks - ndbytes; % number of padding bytes + if npbytes + y(end-npbytes+1 : end) = '='; % '=' is used for padding + end + + if isempty(eol) + + % reshape to a row vector + y = reshape(y, [1, nebytes]); + + else + + nlines = ceil(nebytes / 76); % number of lines + neolbytes = length(eol); % number of bytes in eol string + + % pad data so it becomes a multiple of 76 elements + y(nebytes + 1 : 76 * nlines) = 0; + y = reshape(y, 76, nlines); + + % insert eol strings + eol = eol(:); + y(end + 1 : end + neolbytes, :) = eol(:, ones(1, nlines)); + + % remove padding, but keep the last eol string + m = nebytes + neolbytes * (nlines - 1); + n = (76+neolbytes)*nlines - neolbytes; + y(m+1 : n) = ''; + + % extract and reshape to row vector + y = reshape(y, 1, m+neolbytes); + + end + + % output is a character array + y = char(y); diff --git a/external/fieldtrip/utilities/private/dataset2files.m b/external/fieldtrip/utilities/private/dataset2files.m index dce9ecc6..a407a4bf 100644 --- a/external/fieldtrip/utilities/private/dataset2files.m +++ b/external/fieldtrip/utilities/private/dataset2files.m @@ -24,7 +24,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: dataset2files.m 10381 2015-05-07 07:31:10Z roboos $ +% $Id: dataset2files.m 10624 2015-08-24 21:13:37Z roboos $ persistent previous_argin previous_argout @@ -99,12 +99,14 @@ case 'brainvision_vhdr' [path, file, ext] = fileparts(filename); headerfile = fullfile(path, [file '.vhdr']); - if exist(fullfile(path, [file '.eeg'])) + if exist(fullfile(path, [file '.eeg']), 'file') datafile = fullfile(path, [file '.eeg']); - elseif exist(fullfile(path, [file '.seg'])) + elseif exist(fullfile(path, [file '.seg']), 'file') datafile = fullfile(path, [file '.seg']); - elseif exist(fullfile(path, [file '.dat'])) + elseif exist(fullfile(path, [file '.dat']), 'file') datafile = fullfile(path, [file '.dat']); + else + error('cannot determine the data file that corresponds to %s', filename); end case 'brainvision_eeg' [path, file, ext] = fileparts(filename); diff --git a/external/fieldtrip/utilities/private/fixdimord.m b/external/fieldtrip/utilities/private/fixdimord.m index c2b82bbb..a760e280 100644 --- a/external/fieldtrip/utilities/private/fixdimord.m +++ b/external/fieldtrip/utilities/private/fixdimord.m @@ -44,7 +44,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: fixdimord.m 9972 2014-11-19 08:09:34Z roboos $ +% $Id: fixdimord.m 10451 2015-06-10 22:00:07Z roboos $ % if nargin<2, keepsourcedimord = 0; end % @@ -147,7 +147,7 @@ case {'voxel' 'vox' 'repl' 'wcond'} % these are used in some fieldtrip functions, but are not considered standard - warning_once('unexpected dimord "%s"', data.dimord); + ft_warning('unexpected dimord "%s"', data.dimord); case {'pos'} % this is for source data on a 3-d grid, a cortical sheet, or unstructured positions diff --git a/external/fieldtrip/utilities/private/fixname.m b/external/fieldtrip/utilities/private/fixname.m index b93dd7d1..8e3d4133 100644 --- a/external/fieldtrip/utilities/private/fixname.m +++ b/external/fieldtrip/utilities/private/fixname.m @@ -8,7 +8,7 @@ % str = fixname(str) % % -% MATLAB 2014a introduces the matlab.lang.makeValidName and +% MATLAB 2014a introduces the matlab.lang.makeValidName and % matlab.lang.makeUniqueStrings functions for constructing unique MATLAB identifiers, % but this particular implementation also works with older MATLAB versions. % @@ -32,7 +32,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: fixname.m 9795 2014-09-11 13:02:30Z jansch $ +% $Id: fixname.m 10455 2015-06-11 21:30:27Z roboos $ str = lower(str); str(regexp(str,'\W')) = '_'; @@ -40,8 +40,14 @@ while(str(1) == '_'), str = str(2:end); end; % remove all underscore at the begin of the string while(str(end) == '_'), str = str(1:end-1); end; % remove all underscore at the end of the string -%if ~isempty(str2double(str(1))) && ~isequal(str(1), 'i') if int8(str(1))<58 && int8(str(1))>47 % the string begins with a digit, prepend an 'x' str = ['x' str]; end + +% truncate the string if it's too long: MATLAB maximizes the string length to 63 +% characters (and throws a warning when truncating) +if numel(str)>63 + ft_warning(sprintf('%s exceeds MATLAB''s maximum name length of 63 characters and has been truncated to %s',str,str(1:63))); + str = str(1:63); +end diff --git a/external/fieldtrip/utilities/private/fixpos.m b/external/fieldtrip/utilities/private/fixpos.m index 7028f01f..3288c66c 100644 --- a/external/fieldtrip/utilities/private/fixpos.m +++ b/external/fieldtrip/utilities/private/fixpos.m @@ -6,16 +6,24 @@ recurse = 1; end +if ~isa(data, 'struct') + return; +end + if numel(data)>1 % loop over all individual elements + clear tmp for i=1:numel(data) - data(i) = fixpos(data(i)); + % this is to prevent an "Subscripted assignment between dissimilar structures" error + tmp(i) = fixpos(data(i)); end + data = tmp; + clear tmp return end % replace pnt by pos -if isfield(data, 'pnt') && ~isfield(data, 'label') +if isfield(data, 'pnt') data.pos = data.pnt; data = rmfield(data, 'pnt'); end diff --git a/external/fieldtrip/utilities/private/fixsampleinfo.m b/external/fieldtrip/utilities/private/fixsampleinfo.m index 5fe58600..d82ec5f6 100644 --- a/external/fieldtrip/utilities/private/fixsampleinfo.m +++ b/external/fieldtrip/utilities/private/fixsampleinfo.m @@ -63,20 +63,20 @@ end if isempty(trl) - warning_once('the data does not contain a trial definition'); + ft_warning('the data does not contain a trial definition'); elseif ~isempty(trl) && size(trl,1)~=numel(nsmp) - warning_once('the trial definition in the configuration is inconsistent with the actual data'); + ft_warning('the trial definition in the configuration is inconsistent with the actual data'); trl = []; elseif size(trl,1)~=ntrial - warning_once('the trial definition in the configuration is inconsistent with the actual data'); + ft_warning('the trial definition in the configuration is inconsistent with the actual data'); trl = []; elseif nsmp~=(trl(:,2)-trl(:,1)+1) - warning_once('the trial definition in the configuration is inconsistent with the actual data'); + ft_warning('the trial definition in the configuration is inconsistent with the actual data'); trl = []; end if isempty(trl) || ~all(nsmp==trl(:,2)-trl(:,1)+1) - warning_once('reconstructing sampleinfo by assuming that the trials are consecutive segments of a continuous recording'); + ft_warning('reconstructing sampleinfo by assuming that the trials are consecutive segments of a continuous recording'); % construct a trial definition on the fly, assume that the trials are % consecutive segments of a continuous recording if ntrial==1, @@ -103,7 +103,7 @@ data.sampleinfo = trl(:, 1:2); elseif ~isfield(data, 'sampleinfo') && isempty(trl) % this is probably an unreachable statement - warning_once('failed to create sampleinfo field'); + ft_warning('failed to create sampleinfo field'); end if (~isfield(data, 'trialinfo') || isempty(data.trialinfo)) && ~isempty(trl) && size(trl, 2) > 3, diff --git a/external/fieldtrip/utilities/private/ft_findcfg.m b/external/fieldtrip/utilities/private/ft_findcfg.m index 18ebda5a..56405e79 100644 --- a/external/fieldtrip/utilities/private/ft_findcfg.m +++ b/external/fieldtrip/utilities/private/ft_findcfg.m @@ -4,12 +4,14 @@ % or in the nested previous cfgs % % Use as -% [val] = ft_findcfg(cfg, var) +% val = ft_findcfg(cfg, var) % where the name of the variable should be specified as string. % % e.g. % trl = ft_findcfg(cfg, 'trl') % event = ft_findcfg(cfg, 'event') +% +% See also FT_GETOPT, FT_CFG2KEYVAL % Copyright (C) 2006, Robert Oostenveld % @@ -29,7 +31,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: ft_findcfg.m 9792 2014-09-11 09:50:15Z jansch $ +% $Id: ft_findcfg.m 11053 2016-01-09 17:51:21Z roboos $ % if var(1)~='.' % var = ['.' var]; @@ -61,4 +63,3 @@ break end end - diff --git a/external/fieldtrip/utilities/private/ft_platform_supports.m b/external/fieldtrip/utilities/private/ft_platform_supports.m new file mode 100644 index 00000000..a160fa7d --- /dev/null +++ b/external/fieldtrip/utilities/private/ft_platform_supports.m @@ -0,0 +1,244 @@ +function tf = ft_platform_supports(what,varargin) + +% FT_PLATFORM_SUPPORTS returns a boolean indicating whether the current platform +% supports a specific capability +% +% Usage: +% tf = ft_platform_supports(what) +% tf = ft_platform_supports('matlabversion', min_version, max_version) +% +% The following values are allowed for the 'what' parameter: +% value means that the following is supported: +% +% 'which-all' which(...,'all') +% 'exists-in-private-directory' exists(...) will look in the /private +% subdirectory to see if a file exists +% 'onCleanup' onCleanup(...) +% 'int32_logical_operations' bitand(a,b) with a, b of type int32 +% 'graphics_objects' graphics sysem is object-oriented +% 'libmx_c_interface' libmx is supported through mex in the +% C-language (recent Matlab versions only +% support C++) +% 'program_invocation_name' program_invocation_name() (GNU Octave) +% 'singleCompThread' start Matlab with -singleCompThread +% 'nosplash' -nosplash +% 'nodisplay' -nodisplay +% 'nojvm' -nojvm +% 'no-gui' start GNU Octave with --no-gui +% 'RandStream.setGlobalStream' RandStream.setGlobalStream(...) +% 'RandStream.setDefaultStream' RandStream.setDefaultStream(...) +% 'rng' rng(...) +% 'rand-state' rand('state') +% 'urlread-timeout' urlread(..., 'Timeout', t) + +if ~ischar(what) + error('first argument must be a string'); +end + +switch what + case 'matlabversion' + tf = is_matlab() && matlabversion(varargin{:}); + + case 'exists-in-private-directory' + tf = is_matlab(); + + case 'which-all' + tf = is_matlab(); + + case 'onCleanup' + tf = is_octave() || matlabversion(7.8, Inf); + + case 'int32_logical_operations' + % earlier version of Matlab don't support bitand (and similar) + % operations on int32 + tf = is_octave() || ~matlabversion(-inf, '2012a'); + + case 'graphics_objects' + % introduced in Matlab 2014b, graphics is handled through objects; + % previous versions use numeric handles + tf = is_matlab() && matlabversion('2014b', Inf); + + case 'libmx_c_interface' + % removed after 2013b + tf = matlabversion(-Inf, '2013b'); + + case 'program_invocation_name' + % Octave supports program_invocation_name, which returns the path + % of the binary that was run to start Octave + tf = is_octave(); + + case 'singleCompThread' + tf = is_matlab() && matlabversion(7.8, inf); + + case {'nosplash','nodisplay','nojvm'} + % Only on Matlab + tf = is_matlab(); + + case 'no-gui' + % Only on Octave + tf = is_octave(); + + case 'RandStream.setDefaultStream' + tf = is_matlab() && matlabversion('2008b', '2011b'); + + case 'RandStream.setGlobalStream' + tf = is_matlab() && matlabversion('2012a', inf); + + case 'randomized_PRNG_on_startup' + tf = is_octave() || ~matlabversion(-Inf,'7.3'); + + case 'rng' + % recent Matlab versions + tf = is_matlab() && matlabversion('7.12',Inf); + + case 'rand-state' + % GNU Octave + tf = is_octave(); + + case 'urlread-timeout' + tf = matlabversion('2012b',Inf); + + otherwise + error('unsupported value for first argument: %s', what); + +end % switch + +end % function + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function tf = is_matlab() +tf = ~is_octave(); +end % function + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function tf = is_octave() +persistent cached_tf; + +if isempty(cached_tf) + cached_tf = logical(exist('OCTAVE_VERSION', 'builtin')); +end + +tf = cached_tf; +end % function + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% SUBFUNCTION +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function [inInterval] = matlabversion(min, max) + +% MATLABVERSION checks if the current MATLAB version is within the interval +% specified by min and max. +% +% Use, e.g., as: +% if matlabversion(7.0, 7.9) +% % do something +% end +% +% Both strings and numbers, as well as infinities, are supported, eg.: +% matlabversion(7.1, 7.9) % is version between 7.1 and 7.9? +% matlabversion(6, '7.10') % is version between 6 and 7.10? (note: '7.10', not 7.10) +% matlabversion(-Inf, 7.6) % is version <= 7.6? +% matlabversion('2009b') % exactly 2009b +% matlabversion('2008b', '2010a') % between two versions +% matlabversion('2008b', Inf) % from a version onwards +% etc. +% +% See also VERSION, VER, VERLESSTHAN + +% Copyright (C) 2006, Robert Oostenveld +% Copyright (C) 2010, Eelke Spaak +% +% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id: ft_platform_supports.m 10466 2015-06-22 16:39:29Z roboos $ + +% this does not change over subsequent calls, making it persistent speeds it up +persistent curVer + +if nargin<2 + max = min; +end + +if isempty(curVer) + curVer = version(); +end + +if ((ischar(min) && isempty(str2num(min))) || (ischar(max) && isempty(str2num(max)))) + % perform comparison with respect to release string + + ind = strfind(curVer, '(R'); + [year, ab] = parseMatlabRelease(curVer((ind + 2):(numel(curVer) - 1))); + + [minY, minAb] = parseMatlabRelease(min); + [maxY, maxAb] = parseMatlabRelease(max); + + inInterval = orderedComparison(minY, minAb, maxY, maxAb, year, ab); +else % perform comparison with respect to version number + [major, minor] = parseMatlabVersion(curVer); + [minMajor, minMinor] = parseMatlabVersion(min); + [maxMajor, maxMinor] = parseMatlabVersion(max); + + inInterval = orderedComparison(minMajor, minMinor, maxMajor, maxMinor, major, minor); +end + + function [year, ab] = parseMatlabRelease(str) + if (str == Inf) + year = Inf; ab = Inf; + elseif (str == -Inf) + year = -Inf; ab = -Inf; + else + year = str2num(str(1:4)); + ab = str(5); + end + end + + function [major, minor] = parseMatlabVersion(ver) + if (ver == Inf) + major = Inf; minor = Inf; + elseif (ver == -Inf) + major = -Inf; minor = -Inf; + elseif (isnumeric(ver)) + major = floor(ver); + minor = int8((ver - floor(ver)) * 10); + else % ver is string (e.g. '7.10'), parse accordingly + [major, rest] = strtok(ver, '.'); + major = str2num(major); + minor = str2num(strtok(rest, '.')); + end + end + +% checks if testA is in interval (lowerA,upperA); if at edges, checks if testB is in interval (lowerB,upperB). + function inInterval = orderedComparison(lowerA, lowerB, upperA, upperB, testA, testB) + if (testA < lowerA || testA > upperA) + inInterval = false; + else + inInterval = true; + if (testA == lowerA) + inInterval = inInterval && (testB >= lowerB); + end + + if (testA == upperA) + inInterval = inInterval && (testB <= upperB); + end + end + end + +end % function diff --git a/external/fieldtrip/utilities/private/ft_postamble_debug.m b/external/fieldtrip/utilities/private/ft_postamble_debug.m index 97895150..68a98fe6 100644 --- a/external/fieldtrip/utilities/private/ft_postamble_debug.m +++ b/external/fieldtrip/utilities/private/ft_postamble_debug.m @@ -23,8 +23,19 @@ % these are still needed by the cleanup function otherwise - % this results in the cleanup function doing nothing - Ce9dei2ZOo_debug = 'no'; - Ce9dei2ZOo_funname = []; - Ce9dei2ZOo_argin = []; + % stack(1) is this script + % stack(2) is the calling ft_postamble function + % stack(3) is the main FieldTrip function that we are interested in + Ce9dei2ZOx_funname = dbstack; + Ce9dei2ZOx_funname = Ce9dei2ZOx_funname(3).name; + + if isequal(Ce9dei2ZOx_funname, Ce9dei2ZOo_funname) + % this results in the cleanup function doing nothing + Ce9dei2ZOo_debug = 'no'; + Ce9dei2ZOo_funname = []; + Ce9dei2ZOo_argin = []; + else + % this happens for nested fieldtrip functions, e.g. when + % ft_selectdata is called by another high-level function + end end diff --git a/external/fieldtrip/utilities/private/ft_postamble_history.m b/external/fieldtrip/utilities/private/ft_postamble_history.m index 24d3ded9..92be173b 100644 --- a/external/fieldtrip/utilities/private/ft_postamble_history.m +++ b/external/fieldtrip/utilities/private/ft_postamble_history.m @@ -25,7 +25,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: ft_postamble_history.m 9561 2014-05-21 06:50:39Z roboos $ +% $Id: ft_postamble_history.m 10451 2015-06-10 22:00:07Z roboos $ global ft_default @@ -56,4 +56,4 @@ clear tmpindx % clear warnings from ft_default, so that they don't end up in the next cfg -warning_once('-clear'); +ft_warning('-clear'); diff --git a/external/fieldtrip/utilities/private/ft_preamble_init.m b/external/fieldtrip/utilities/private/ft_preamble_init.m index 633dd931..bac4b0a3 100644 --- a/external/fieldtrip/utilities/private/ft_preamble_init.m +++ b/external/fieldtrip/utilities/private/ft_preamble_init.m @@ -1,10 +1,12 @@ -% FT_PREAMBLE_INIT is a helper script that display the calling-function's -% help in case the user did not specify any input argument. This can be -% used in all fieldtrip main functions that take at least a cfg input -% argument, and most also take one or multiple data structures. +% FT_PREAMBLE_INIT is a helper script that is used at the start of all FieldTrip main +% functions. It checks whether the user specified at lease one input arguments (i.e. +% the cfg) or shows the help of the calling function. It checks whether the output file +% already exists and whether it is OK to overwrite it. It tracks the function call. % % Use as % ft_preamble init +% +% See also FT_TRACKUSAGE % Copyright (C) 2011-2012, Robert Oostenveld, DCCN % @@ -24,7 +26,10 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: ft_preamble_init.m 9561 2014-05-21 06:50:39Z roboos $ +% $Id: ft_preamble_init.m 10896 2015-11-17 12:31:35Z roboos $ + +% disabled for now, see further down +global ft_default if nargin==0 stack = dbstack('-completenames'); @@ -38,12 +43,25 @@ msg.identifier = ''; msg.stack = stack; error(msg); -end +end % if nargin + +% this script requires some options that can be user-specified, but otherwise are obtained from ft_default +% merge the default options into the configuration, except the preamble field which is used for passing arguments +cfg = mergeconfig(cfg, rmfield(ft_default, 'preamble')); % determine whether function execution should be aborted or continued -if isfield(cfg, 'outputfile') && ~isempty(cfg.outputfile) && isfield(cfg, 'outputfilepresent') && ~isempty(cfg.outputfilepresent) +if isfield(cfg, 'outputfile') && ~isempty(cfg.outputfile) + assert(any(strcmp(fieldnames(cfg), 'outputfilepresent')), 'cfg.outputfilepresent is a required option, please see FT_DEFAULTS'); % check whether the output file already exists - if ~exist(cfg.outputfile, 'file') + [p, f, x] = fileparts(cfg.outputfile); + if isempty(p) + % the relative path was speciield + outputfile = fullfile(pwd, cfg.outputfile); + else + % the absolute path was specified + outputfile = cfg.outputfile; + end + if ~exist(outputfile, 'file') abort = false; else % the output file exists, determine how to deal with it @@ -58,16 +76,28 @@ warning('output file %s is already present: aborting function execution', cfg.outputfile); abort = true; end - case 'error' - error('output file %s is already present', cfg.outputfile); case 'overwrite' warning('output file %s is already present: it will be overwritten', cfg.outputfile); abort = false; + case 'error' + error('output file %s is already present', cfg.outputfile); otherwise error('invalid option for cfg.outputfilepresent'); end % case end else + % there is no reason to abort execution abort = false; -end +end % if outputfile +if false + % this is currently generating too much data and therefore disabled + if isfield(ft_default, 'trackusage') && ~(isequal(ft_default.trackusage, false) || isequal(ft_default.trackusage, 'no') || isequal(ft_default.trackusage, 'off')) + % track the usage of the calling function + stack = dbstack('-completenames'); + % stack(1) is this script + % stack(2) is the calling ft_postamble function + % stack(3) is the main FieldTrip function that we are interested in + ft_trackusage('function call', 'function', stack(3).name); + end % if trackusage +end diff --git a/external/fieldtrip/utilities/private/ft_preamble_provenance.m b/external/fieldtrip/utilities/private/ft_preamble_provenance.m index c9355a67..692da49d 100644 --- a/external/fieldtrip/utilities/private/ft_preamble_provenance.m +++ b/external/fieldtrip/utilities/private/ft_preamble_provenance.m @@ -34,7 +34,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: ft_preamble_provenance.m 9900 2014-10-13 15:06:43Z roboos $ +% $Id: ft_preamble_provenance.m 10765 2015-10-09 18:10:47Z roboos $ % Record the start time and memory. These are used by ft_postamble_callinfo, which % stores them in the output cfg.callinfo. In the mean time, they are stored in the @@ -58,7 +58,11 @@ if isequal(ft_default.preamble, {'varargin'}) tmpargin = varargin; else - tmpargin = cellfun(@eval, ft_default.preamble, 'UniformOutput', false); + isvar = cellfun(@(x) exist(x, 'var')==1, ft_default.preamble); + tmpargin = cellfun(@eval, ft_default.preamble(isvar), 'UniformOutput', false); + tmpargin( isvar) = tmpargin; + tmpargin(~isvar) = {[]}; + clear isvar end cfg.callinfo.inputhash = cell(1,numel(tmpargin)); for iargin = 1:numel(tmpargin) diff --git a/external/fieldtrip/utilities/private/ft_preamble_trackconfig.m b/external/fieldtrip/utilities/private/ft_preamble_trackconfig.m index d83462b0..6b1df648 100644 --- a/external/fieldtrip/utilities/private/ft_preamble_trackconfig.m +++ b/external/fieldtrip/utilities/private/ft_preamble_trackconfig.m @@ -27,15 +27,11 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: ft_preamble_trackconfig.m 9561 2014-05-21 06:50:39Z roboos $ +% $Id: ft_preamble_trackconfig.m 10896 2015-11-17 12:31:35Z roboos $ % otherwise the empty field would end up in the output cfg global ft_default -ft_default = rmfield(ft_default, 'preamble'); % most fieldtrip functions should allow for configuration tracking, except for % the functions that take a cfg as input and return a cfg as output cfg = ft_checkconfig(cfg, 'trackconfig', 'on'); - -% the calling ft_preable expects it to be present -ft_default.preamble = {}; diff --git a/external/fieldtrip/external/stats/randsample.m b/external/fieldtrip/utilities/private/getaddress.m similarity index 50% rename from external/fieldtrip/external/stats/randsample.m rename to external/fieldtrip/utilities/private/getaddress.m index 055ece31..8b342669 100644 --- a/external/fieldtrip/external/stats/randsample.m +++ b/external/fieldtrip/utilities/private/getaddress.m @@ -1,18 +1,15 @@ -function [y] = randsample(x, k) +function address = getip(hostname) -% RANDSAMPLE Random sample, with or without replacement. This is a drop-in -% replacement for the matlab funnction with the same name. Not all options -% are supported, an error will be issued if needed. +% GETADDRESS returns the IP address % -% Y = RANDSAMPLE(N,K) returns Y as a 1-by-K vector of values sampled -% uniformly at random, without replacement, from the integers 1:N. +% Use as +% address = getaddress(); +% or +% address = getaddress(hostname); % -% Y = RANDSAMPLE(POPULATION,K) returns K values sampled uniformly at -% random, without replacement, from the values in the vector POPULATION. -% -% See also RAND, RANDPERM. +% See also GETUSERNAME, GETHOSTNAME -% Copyright (C) 2007, Robert Oostenveld +% Copyright (C) 2015, Robert Oostenveld % % This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip % for the documentation and details. @@ -30,15 +27,27 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: randsample.m 8410 2013-08-21 14:16:44Z eelspa $ +% $Id: getaddress.m 10452 2015-06-11 02:14:45Z roboos $ + +% this is to speed up subsequent calls +persistent previous_argin previous_argout + +if nargin==0 + hostname = []; +end -if nargin>2 - error('only two input variables are supported'); +if ~isempty(previous_argout) && isequal(hostname, previous_argin) + address = previous_argout; + return end -if length(x)==1 && isnumeric(x) - x = 1:x; +if ~isempty(hostname) + address = java.net.InetAddress.getByName(hostname); +else + address = java.net.InetAddress.getLocalHost; end +address = char(address.getHostAddress); -sel = ceil(rand(1,k)*length(x)); -y = x(sel); +% remember for subsequent calls +previous_argin = hostname; +previous_argout = address; diff --git a/external/fieldtrip/utilities/private/getdatfield.m b/external/fieldtrip/utilities/private/getdatfield.m new file mode 100644 index 00000000..6849eee2 --- /dev/null +++ b/external/fieldtrip/utilities/private/getdatfield.m @@ -0,0 +1,32 @@ +function [datfield, dimord] = getdatfield(data) + +% GETDATFIELD +% +% Use as +% [datfield, dimord] = getdatfield(data) +% where the output arguments are cell-arrays. +% +% See also GETDIMORD, GETDIMSIZ + +datfield = fieldnames(data); + +% these descriptive fields are cell-arrays and not treated as data +% the descriptive fields such as time and freq can be treated as data +xtrafield = {'label' 'labelcmb'}; +datfield = setdiff(datfield, xtrafield); + +xtrafield = {'cfg' 'hdr' 'fsample' 'fsampleorig' 'grad' 'elec' 'opto' 'transform' 'dim' 'unit' 'coordsys' 'tri' 'tet' 'hex'}; +datfield = setdiff(datfield, xtrafield); + +orgdim1 = datfield(~cellfun(@isempty, regexp(datfield, 'label$'))); % xxxlabel +datfield = setdiff(datfield, orgdim1); +datfield = datfield(:)'; + +orgdim1 = datfield(~cellfun(@isempty, regexp(datfield, 'dimord$'))); % xxxdimord +datfield = setdiff(datfield, orgdim1); +datfield = datfield(:)'; + +dimord = cell(size(datfield)); +for i=1:length(datfield) + dimord{i} = getdimord(data, datfield{i}); +end diff --git a/external/fieldtrip/utilities/private/getdimord.m b/external/fieldtrip/utilities/private/getdimord.m index 43fd23f9..e26bdc25 100644 --- a/external/fieldtrip/utilities/private/getdimord.m +++ b/external/fieldtrip/utilities/private/getdimord.m @@ -5,7 +5,7 @@ % Use as % dimord = getdimord(data, field) % -% See also GETDIMSIZ +% See also GETDIMSIZ, GETDATFIELD if ~isfield(data, field) && isfield(data, 'avg') && isfield(data.avg, field) field = ['avg.' field]; @@ -57,6 +57,9 @@ ntopochan = inf; nspike = inf; % this is only for the first spike channel nlag = nan; +ndim1 = nan; +ndim2 = nan; +ndim3 = nan; % use an anonymous function assign = @(var, val) assignin('caller', var, val); @@ -119,6 +122,12 @@ npos = prod(data.dim); end +if isfield(data, 'dim') + ndim1 = data.dim(1); + ndim2 = data.dim(2); + ndim3 = data.dim(3); +end + if isfield(data, 'csdlabel') % this is used in PCC beamformers if length(data.csdlabel)==npos @@ -154,8 +163,8 @@ % determine the size of the actual data datsiz = getdimsiz(data, field); -tok = {'subj' 'rpt' 'rpttap' 'chan' 'chancmb' 'freq' 'time' 'pos' 'ori' 'topochan' 'lag'}; -siz = [nsubj nrpt nrpttap nchan nchancmb nfreq ntime npos nori ntopochan nlag]; +tok = {'subj' 'rpt' 'rpttap' 'chan' 'chancmb' 'freq' 'time' 'pos' 'ori' 'topochan' 'lag' 'dim1' 'dim2' 'dim3'}; +siz = [nsubj nrpt nrpttap nchan nchancmb nfreq ntime npos nori ntopochan nlag ndim1 ndim2 ndim3]; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % ATTEMPT 2: a general dimord is present and might apply @@ -200,6 +209,11 @@ % note that the case for a cell dimension (typically pos) is handled at % the end of this section + case {'pos'} + if isequalwithoutnans(datsiz, [npos 3]) + dimord = 'pos_unknown'; + end + case {'individual'} if isequalwithoutnans(datsiz, [nsubj nchan ntime]) dimord = 'subj_chan_time'; @@ -417,7 +431,7 @@ dimord = '{chan}_spike'; elseif ft_datatype(data, 'raw') && iscell(data.(field)) && datsiz(1)==nrpt dimord = '{rpt}_time'; - elseif isvector(data.(field)) && isequal(datsiz, [1 ntime]) + elseif isvector(data.(field)) && isequal(datsiz, [1 ntime ones(1,numel(datsiz)-2)]) dimord = 'time'; end @@ -428,8 +442,7 @@ otherwise if isfield(data, 'dim') && isequal(datsiz, data.dim) - % FIXME is this the desired dimord for volume data? A dimord of vox or voxel is not recommended according to fixdimord. - dimord = 'pos'; + dimord = 'dim1_dim2_dim3'; end end % switch field @@ -485,6 +498,9 @@ dimtok(datsiz==nchan) = {'chan'}; dimtok(datsiz==nfreq) = {'freq'}; dimtok(datsiz==ntime) = {'time'}; + dimtok(datsiz==ndim1) = {'dim1'}; + dimtok(datsiz==ndim2) = {'dim2'}; + dimtok(datsiz==ndim3) = {'dim3'}; if isempty(dimtok{end}) && datsiz(end)==1 % remove the unknown trailing singleton dimension @@ -505,8 +521,17 @@ end end % if dimord does not exist +if ~exist('dimord', 'var') + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % ATTEMPT 6: check whether it is a 3-D volume + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if isequal(datsiz, [ndim1 ndim2 ndim3]) + dimord = 'dim1_dim2_dim3'; + end +end % if dimord does not exist + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% ATTEMPT 6: return "unknown" for all unknown dimensions +% FINAL RESORT: return "unknown" for all unknown dimensions %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% if ~exist('dimord', 'var') % this should not happen diff --git a/external/fieldtrip/utilities/private/getdimsiz.m b/external/fieldtrip/utilities/private/getdimsiz.m index 16582648..90e27771 100644 --- a/external/fieldtrip/utilities/private/getdimsiz.m +++ b/external/fieldtrip/utilities/private/getdimsiz.m @@ -5,7 +5,17 @@ % Use as % dimsiz = getdimsiz(data, field) % -% See also GETDIMORD +% If the length of the vector that is returned is smaller than the +% number of dimensions that you would expect from GETDIMORD, you +% should assume that it has trailing singleton dimensions. +% +% Example use +% dimord = getdimord(datastructure, fieldname); +% dimtok = tokenize(dimord, '_'); +% dimsiz = getdimsiz(datastructure, fieldname); +% dimsiz(end+1:length(dimtok)) = 1; % there can be additional trailing singleton dimensions +% +% See also GETDIMORD, GETDATFIELD if ~isfield(data, field) && isfield(data, 'avg') && isfield(data.avg, field) field = ['avg.' field]; @@ -42,7 +52,15 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% function siz = cellmatsize(x) if iscell(x) - cellsize = numel(x); % the number of elements in the cell-array + if isempty(x) + siz = 0; + return % nothing else to do + elseif isvector(x) + cellsize = numel(x); % the number of elements in the cell-array + else + cellsize = size(x); + x = x(:); % convert to vector for further size detection + end [dum, indx] = max(cellfun(@numel, x)); matsize = size(x{indx}); % the size of the content of the cell-array siz = [cellsize matsize]; % concatenate the two diff --git a/external/fieldtrip/utilities/private/gethostname.m b/external/fieldtrip/utilities/private/gethostname.m index d62bfc35..89539ac0 100644 --- a/external/fieldtrip/utilities/private/gethostname.m +++ b/external/fieldtrip/utilities/private/gethostname.m @@ -1,9 +1,11 @@ function host = gethostname() -% HOSTNAME +% HOSTNAME returns the hostname of this computer % % Use as % str = hostname; +% +% See also GETUSERNAME, GETADDRESS % Copyright (C) 2011, Eelke Spaak % @@ -23,7 +25,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: gethostname.m 8776 2013-11-14 09:04:48Z roboos $ +% $Id: gethostname.m 10452 2015-06-11 02:14:45Z roboos $ % this is to speed up subsequent calls persistent previous_argout diff --git a/external/fieldtrip/utilities/private/getusername.m b/external/fieldtrip/utilities/private/getusername.m index bc7a6205..5d1ef945 100644 --- a/external/fieldtrip/utilities/private/getusername.m +++ b/external/fieldtrip/utilities/private/getusername.m @@ -3,7 +3,7 @@ % GETUSERNAME % % Use as -% str = username; +% str = getusername(); % Copyright (C) 2011-2012, Robert Oostenveld % @@ -23,7 +23,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: getusername.m 7123 2012-12-06 21:21:38Z roboos $ +% $Id: getusername.m 10453 2015-06-11 02:14:49Z roboos $ % this is to speed up subsequent calls persistent previous_argout diff --git a/external/fieldtrip/utilities/private/icosahedron.m b/external/fieldtrip/utilities/private/icosahedron.m index 92d14ed8..06560f93 100644 --- a/external/fieldtrip/utilities/private/icosahedron.m +++ b/external/fieldtrip/utilities/private/icosahedron.m @@ -1,8 +1,8 @@ -function [pnt, tri] = icosahedron() +function [pos, tri] = icosahedron() % ICOSAHEDRON creates an icosahedron % -% [pnt, tri] = icosahedron +% [pos, tri] = icosahedron % creates an icosahedron with 12 vertices and 20 triangles % % See also OCTAHEDRON, ICOSAHEDRON42, ICOSAHEDRON162, ICOSAHEDRON642, ICOSAHEDRON2562 @@ -25,7 +25,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: icosahedron.m 9664 2014-06-22 07:06:29Z roboos $ +% $Id: icosahedron.m 10772 2015-10-14 07:36:57Z roboos $ tri = [ 1 2 3 @@ -50,20 +50,20 @@ 12 7 11 ]; -pnt = zeros(12, 3); +pos = zeros(12, 3); rho=0.4*sqrt(5); phi=2*pi*(0:4)/5; -pnt( 1, :) = [0 0 1]; % top point +pos( 1, :) = [0 0 1]; % top point -pnt(2:6, 1) = rho*cos(phi)'; -pnt(2:6, 2) = rho*sin(phi)'; -pnt(2:6, 3) = rho/2; +pos(2:6, 1) = rho*cos(phi)'; +pos(2:6, 2) = rho*sin(phi)'; +pos(2:6, 3) = rho/2; -pnt(7:11, 1) = rho*cos(phi - pi/5)'; -pnt(7:11, 2) = rho*sin(phi - pi/5)'; -pnt(7:11, 3) = -rho/2; +pos(7:11, 1) = rho*cos(phi - pi/5)'; +pos(7:11, 2) = rho*sin(phi - pi/5)'; +pos(7:11, 3) = -rho/2; -pnt(12, :) = [0 0 -1]; % bottom point +pos(12, :) = [0 0 -1]; % bottom point diff --git a/external/fieldtrip/utilities/private/icosahedron42.m b/external/fieldtrip/utilities/private/icosahedron42.m index 5aa47e52..5a848482 100644 --- a/external/fieldtrip/utilities/private/icosahedron42.m +++ b/external/fieldtrip/utilities/private/icosahedron42.m @@ -1,4 +1,4 @@ -function [pnt, tri] = icosahedron42() +function [pos, tri] = icosahedron42() % ICOSAHEDRON42 creates a 1-fold refined icosahedron @@ -20,9 +20,9 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: icosahedron42.m 9664 2014-06-22 07:06:29Z roboos $ +% $Id: icosahedron42.m 10772 2015-10-14 07:36:57Z roboos $ -[pnt, tri] = icosahedron; -[pnt, tri] = refine(pnt, tri); +[pos, tri] = icosahedron; +[pos, tri] = refine(pos, tri); -pnt = pnt ./ repmat(sqrt(sum(pnt.^2,2)), 1,3); +pos = pos ./ repmat(sqrt(sum(pos.^2,2)), 1,3); diff --git a/external/fieldtrip/utilities/private/lmoutr.mexw32 b/external/fieldtrip/utilities/private/lmoutr.mexw32 index f4d88620..7be79de1 100755 Binary files a/external/fieldtrip/utilities/private/lmoutr.mexw32 and b/external/fieldtrip/utilities/private/lmoutr.mexw32 differ diff --git a/external/fieldtrip/utilities/private/lmoutr.mexw64 b/external/fieldtrip/utilities/private/lmoutr.mexw64 index 62e72e7c..8ad4f571 100755 Binary files a/external/fieldtrip/utilities/private/lmoutr.mexw64 and b/external/fieldtrip/utilities/private/lmoutr.mexw64 differ diff --git a/external/fieldtrip/utilities/private/matlabversion.m b/external/fieldtrip/utilities/private/matlabversion.m deleted file mode 100644 index 87593b91..00000000 --- a/external/fieldtrip/utilities/private/matlabversion.m +++ /dev/null @@ -1,114 +0,0 @@ -function [inInterval] = matlabversion(min, max) - -% MATLABVERSION checks if the current MATLAB version is within the interval -% specified by min and max. -% -% Use, e.g., as: -% if matlabversion(7.0, 7.9) -% % do something -% end -% -% Both strings and numbers, as well as infinities, are supported, eg.: -% matlabversion(7.1, 7.9) % is version between 7.1 and 7.9? -% matlabversion(6, '7.10') % is version between 6 and 7.10? (note: '7.10', not 7.10) -% matlabversion(-Inf, 7.6) % is version <= 7.6? -% matlabversion('2009b') % exactly 2009b -% matlabversion('2008b', '2010a') % between two versions -% matlabversion('2008b', Inf) % from a version onwards -% etc. -% -% See also VERSION, VER, VERLESSTHAN - -% Copyright (C) 2006, Robert Oostenveld -% Copyright (C) 2010, Eelke Spaak -% -% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id: matlabversion.m 10198 2015-02-11 09:36:13Z roboos $ - -% this does not change over subsequent calls, making it persistent speeds it up -persistent curVer - -if nargin<2 - max = min; -end - -if isempty(curVer) - curVer = version(); -end - -if ((ischar(min) && isempty(str2num(min))) || (ischar(max) && isempty(str2num(max)))) - % perform comparison with respect to release string - - ind = strfind(curVer, '(R'); - [year, ab] = parseMatlabRelease(curVer((ind + 2):(numel(curVer) - 1))); - - [minY, minAb] = parseMatlabRelease(min); - [maxY, maxAb] = parseMatlabRelease(max); - - inInterval = orderedComparison(minY, minAb, maxY, maxAb, year, ab); -else % perform comparison with respect to version number - [major, minor] = parseMatlabVersion(curVer); - [minMajor, minMinor] = parseMatlabVersion(min); - [maxMajor, maxMinor] = parseMatlabVersion(max); - - inInterval = orderedComparison(minMajor, minMinor, maxMajor, maxMinor, major, minor); -end - - function [year, ab] = parseMatlabRelease(str) - if (str == Inf) - year = Inf; ab = Inf; - elseif (str == -Inf) - year = -Inf; ab = -Inf; - else - year = str2num(str(1:4)); - ab = str(5); - end - end - - function [major, minor] = parseMatlabVersion(ver) - if (ver == Inf) - major = Inf; minor = Inf; - elseif (ver == -Inf) - major = -Inf; minor = -Inf; - elseif (isnumeric(ver)) - major = floor(ver); - minor = int8((ver - floor(ver)) * 10); - else % ver is string (e.g. '7.10'), parse accordingly - [major, rest] = strtok(ver, '.'); - major = str2num(major); - minor = str2num(strtok(rest, '.')); - end - end - -% checks if testA is in interval (lowerA,upperA); if at edges, checks if testB is in interval (lowerB,upperB). - function inInterval = orderedComparison(lowerA, lowerB, upperA, upperB, testA, testB) - if (testA < lowerA || testA > upperA) - inInterval = false; - else - inInterval = true; - if (testA == lowerA) - inInterval = inInterval && (testB >= lowerB); - end - - if (testA == upperA) - inInterval = inInterval && (testB <= upperB); - end - end - end - -end % function diff --git a/external/fieldtrip/utilities/private/mxSerialize.m b/external/fieldtrip/utilities/private/mxSerialize.m index e579cee7..334956be 100644 --- a/external/fieldtrip/utilities/private/mxSerialize.m +++ b/external/fieldtrip/utilities/private/mxSerialize.m @@ -24,9 +24,9 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: mxSerialize.m 10026 2014-12-08 18:07:07Z eelspa $ +% $Id: mxSerialize.m 10458 2015-06-18 19:53:31Z roboos $ -if matlabversion(-Inf, '2013b') % older than 2014a +if ft_platform_supports('libmx_c_interface') % older than 2014a % use the original implementation of the mex file argout = mxSerialize_c(argin); else diff --git a/external/fieldtrip/utilities/private/parameterselection.m b/external/fieldtrip/utilities/private/parameterselection.m index 052f0cb2..b45aaae9 100644 --- a/external/fieldtrip/utilities/private/parameterselection.m +++ b/external/fieldtrip/utilities/private/parameterselection.m @@ -29,7 +29,7 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: parameterselection.m 9767 2014-08-06 09:51:28Z eelspa $ +% $Id: parameterselection.m 10698 2015-09-28 16:55:45Z roboos $ if ischar(param) param = {param}; % it should be a cell-array @@ -107,6 +107,8 @@ if nels==prod(dim), select{end+1} = param{i}; end + elseif isfield(data, 'dim') && numel(dim)>3 && isequal(dim(1:3), data.dim(1:3)) + select{end+1} = param{i}; end end end diff --git a/external/fieldtrip/utilities/private/ptriproj.mexw32 b/external/fieldtrip/utilities/private/ptriproj.mexw32 index d5fd2caf..1c274de1 100755 Binary files a/external/fieldtrip/utilities/private/ptriproj.mexw32 and b/external/fieldtrip/utilities/private/ptriproj.mexw32 differ diff --git a/external/fieldtrip/utilities/private/ptriproj.mexw64 b/external/fieldtrip/utilities/private/ptriproj.mexw64 index abc1fdcd..997761e6 100755 Binary files a/external/fieldtrip/utilities/private/ptriproj.mexw64 and b/external/fieldtrip/utilities/private/ptriproj.mexw64 differ diff --git a/external/fieldtrip/utilities/private/randomseed.m b/external/fieldtrip/utilities/private/randomseed.m index 6c5fe20b..fc60bf09 100644 --- a/external/fieldtrip/utilities/private/randomseed.m +++ b/external/fieldtrip/utilities/private/randomseed.m @@ -1,19 +1,22 @@ -function state=randomseed(setseed) -% randomseed(seedval) +function state = randomseed(setseed) + +% RANDOMSEED retrieves or sets the random seed, taking into account the different +% MATLAB version specific methods % -% Sets the use of rand/randn/randi to specific state/seed, -% taking into account the different Matlab version specific methods +% Use as +% state = randomseed(setseed) % % INPUT -% setseed: [] does not reset the state, but saves out the state for future use +% setseed [] does not reset the state, but saves out the state for future use % integer seed value to set to specifc state % state vector state value (vector) output from previous call to setting the state % % OUTPUT % state vector of current state (or seed only) -% can be used as input 'setseed' to reset to same state +% +% The output can be used as input re-create the same random number sequence -% Johanna Zumer (2011) +% Copyright (C) 2011, Johanna Zumer % % This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip % for the documentation and details. @@ -31,69 +34,86 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . -if ischar(setseed) - if strcmp(setseed, 'no') - setseed = []; - elseif strcmp(setseed, 'yes') - setseed = sum(100*clock); - else - error('unknown specification for a random seed'); - end +if nargin<1 + setseed = []; end +state = []; -if isempty(setseed) % save out rand state for later use - if matlabversion(-Inf,'7.3') - rand('twister',sum(100*clock)) % can fail otherwise, if first time rand is called per matlab session - state=rand('twister'); - elseif matlabversion('7.4','7.6') - state=rand('twister'); - elseif matlabversion('7.7','7.11') +if isempty(setseed) + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % save out rand state for later use + + if ~ft_platform_supports('randomized_PRNG_on_startup') + % old MATLAB versions (< = v7.3) + rand('twister', sum(100*clock)) % can fail otherwise, if first time rand is called per MATLAB session + end + + if ft_platform_supports('rng') + % Recent MATLAB versions + s = rng; + state = s.State; + elseif ft_platform_supports('rand-state') + % GNU Octave + state = rand('state'); + elseif ft_platform_supports('RandStream.setDefaultStream') + % older MATLAB versions stream = RandStream.getDefaultStream; - state=stream.State; - elseif matlabversion('7.12',Inf) - s=rng; - state=s.State; + state = stream.State; + else + % fallback + state = rand('twister'); end -else % seedval is actual random seed value set by user, OR saved state - if matlabversion(-Inf,'7.6') + +else + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % seedval is the random seed value specified by the user + + if ft_platform_supports('rng') + % Recent MATLAB versions if isscalar(setseed) - state=rand('twister',setseed); + rng(setseed, 'twister') + s = rng; elseif isnumeric(setseed) - % use only the first number of the array - fprintf('the requested random seed seems an array, but only a scalar is supported in this version of MATLAB, using the first entry\n'); - state=rand('twister',setseed(1)); + s.Type = 'twister'; + s.State = setseed; + s.Seed = 0; + rng(s); else - state=rand('twister'); + s.Type = 'Legacy'; + s.Seed = 'Not applicable'; + s.State = setseed; + rng(s); end - elseif matlabversion('7.7','7.11') + state = s.State; + + elseif ft_platform_supports('rand-state') + % GNU Octave if isscalar(setseed) - stream=RandStream('mt19937ar','Seed',setseed); - elseif isnumeric(setseed) - % use only the first number of the array - fprintf('the requested random seed seems an array, but only a scalar is supported in this version of MATLAB, using the first entry\n'); - stream=RandStream('mt19937ar','Seed',setseed(1)); + rand('seed', setseed); else - stream=RandStream.getDefaultStream; - stream.State=setseed; + rand('state', setseed); end - RandStream.setDefaultStream(stream); - state=stream.State; - elseif matlabversion('7.12',Inf) + + elseif ft_platform_supports('RandStream.setDefaultStream') + % older MATLAB versions if isscalar(setseed) - rng(setseed,'twister') - s=rng; - elseif isnumeric(setseed) - s.Type='twister'; - s.State=setseed; - s.Seed=0; - rng(s); + stream = RandStream('mt19937ar', 'Seed', setseed); else - s.Type='Legacy'; - s.Seed='Not applicable'; - s.State=setseed; - rng(s); + stream = RandStream.getDefaultStream; + stream.State = setseed; end - state=s.State; + RandStream.setDefaultStream(stream); + state = stream.State; + else + % fallback + rand('twister', setseed); + state = rand('twister'); end -end; + +end % set or get the seed +if nargin && nargout && isempty(state) + % the output should be the same as the user-supplied input + % this will guarantee tehe same random sequence repeatedly + state = setseed; +end \ No newline at end of file diff --git a/external/fieldtrip/utilities/private/scalingfactor.m b/external/fieldtrip/utilities/private/scalingfactor.m deleted file mode 100644 index b2fddb4e..00000000 --- a/external/fieldtrip/utilities/private/scalingfactor.m +++ /dev/null @@ -1,238 +0,0 @@ -function factor = scalingfactor(old, new) - -% SCALINGFACTOR determines the scaling factor from old to new units. -% -% Use as -% factor = scalingfactor(old, new) -% where old and new are strings that specify the units. -% -% For example -% scalingfactor('m', 'cm') % returns 100 -% scalingfactor('V', 'uV') % returns 1000 -% scalingfactor('T/cm', 'fT/m') % returns 10^15 divided by 10^-2, which is 10^17 -% scalingfactor('cm^2', 'mm^2') % returns 100 -% scalingfactor('1/ms', 'Hz') % returns 1000 -% -% The following fundamental units are supported -% metre m length l (a lowercase L), x, r L -% kilogram kg mass m M -% second s time t T -% ampere A electric current I (an uppercase i) I -% kelvin K thermodynamic temperature T # -% mole mol amount of substance n N -% candela cd luminous intensity Iv (an uppercase i with lowercase non-italicized v subscript) J -% -% The following derived units are supported -% hertz Hz frequency 1/s T-1 -% radian rad angle m/m dimensionless -% steradian sr solid angle m2/m2 dimensionless -% newton N force, weight kg#m/s2 M#L#T-2 -% pascal Pa pressure, stress N/m2 M#L-1#T-2 -% joule J energy, work, heat N#m = C#V = W#s M#L2#T-2 -% coulomb C electric charge or quantity of electricity s#A T#I -% volt V voltage, electrical potential difference, electromotive force W/A = J/C M#L2#T-3#I-1 -% farad F electric capacitance C/V M-1#L-2#T4#I2 -% siemens S electrical conductance 1/# = A/V M-1#L-2#T3#I2 -% weber Wb magnetic flux J/A M#L2#T-2#I-1 -% tesla T magnetic field strength V#s/m2 = Wb/m2 = N/(A#m) M#T-2#I-1 -% henry H inductance V#s/A = Wb/A M#L2#T-2#I-2 -% lumen lm luminous flux cd#sr J -% lux lx illuminance lm/m2 L-2#J -% becquerel Bq radioactivity (decays per unit time) 1/s T-1 -% gray Gy absorbed dose (of ionizing radiation) J/kg L2#T-2 -% sievert Sv equivalent dose (of ionizing radiation) J/kg L2#T-2 -% katal kat catalytic activity mol/s T-1#N -% -% The following derived units are not supported due to potential confusion -% between their ascii character representation -% ohm # electric resistance, impedance, reactance V/A M#L2#T-3#I-2 -% watt W power, radiant flux J/s = V#A M#L2#T-3 -% degree Celsius °C temperature relative to 273.15 K K ? -% -% See also http://en.wikipedia.org/wiki/International_System_of_Units - -% Copyright (C) 2012, Robert Oostenveld -% -% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id: scalingfactor.m 9631 2014-06-16 10:55:43Z vlalit $ - -persistent previous_old previous_new previous_factor - -if isempty(previous_old) - previous_old = {}; - previous_new = {}; - previous_factor = []; -end - -if ~isequal(class(old), class(new)) - error('the input arguments should be of the same class'); -end - -if iscell(old) - factor = cellfun(@scalingfactor, old(:), new(:)); - return -end - -cachehit = strcmp(old, previous_old) & strcmp(new, previous_new); -if any(cachehit) - factor = previous_factor(cachehit); - return -end - -if isequal(old, new) - % this applies regardless of the units - factor = 1; - return -end - -unit = {'m' 'g' 's' 'A' 'K' 'mol' 'cd' 'Hz' 'rad' 'sr' 'N' 'Pa' 'J' 'C' 'V' 'F' 'S' 'Wb' 'T' 'H' 'lm' 'lx' 'Bq' 'Gy' 'Sv' 'kat'}; - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% the following section pertains to checking that units are compatible -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% each of the fundamental units is represented by a prime number -m = 2; -kg = 3; g = 3; % besides representing kg, also represent g to facilitate the processing further down -s = 5; -A = 7; -K = 11; -mol = 13; -cd = 17; -unknown = -1; - -% each of the derives units is represented by a product and/or ratio of prime numbers -Hz = 1/s; -rad = nan; % this is dimensionless, cannot be converted -sr = nan; % this is dimensionless, cannot be converted -N = kg*m/s^2; -Pa = N/m^2; -J = N*m; -C = s*A; -V = J/C; -F = C/V; -S = A/V; -Wb = J/A; -T = Wb/m^2; -H = V*s/A; -lm = cd*sr; -lx = lm/m^2; -Bq = 1/s; -Gy = J/kg; -Sv = J/kg; -kat = mol/s; - -% deal with all possible prefixes -for i=1:length(unit) - eval(sprintf('d%s = %s;', unit{i}, unit{i})); - eval(sprintf('c%s = %s;', unit{i}, unit{i})); - eval(sprintf('m%s = %s;', unit{i}, unit{i})); - eval(sprintf('u%s = %s;', unit{i}, unit{i})); % note that u is used for micro - eval(sprintf('n%s = %s;', unit{i}, unit{i})); - eval(sprintf('p%s = %s;', unit{i}, unit{i})); - eval(sprintf('f%s = %s;', unit{i}, unit{i})); - eval(sprintf('a%s = %s;', unit{i}, unit{i})); - eval(sprintf('z%s = %s;', unit{i}, unit{i})); - eval(sprintf('y%s = %s;', unit{i}, unit{i})); - - eval(sprintf('da%s = %s;', unit{i}, unit{i})); - eval(sprintf('h%s = %s;', unit{i}, unit{i})); - eval(sprintf('k%s = %s;', unit{i}, unit{i})); - eval(sprintf('M%s = %s;', unit{i}, unit{i})); - eval(sprintf('G%s = %s;', unit{i}, unit{i})); - eval(sprintf('T%s = %s;', unit{i}, unit{i})); - eval(sprintf('P%s = %s;', unit{i}, unit{i})); - eval(sprintf('E%s = %s;', unit{i}, unit{i})); - eval(sprintf('Z%s = %s;', unit{i}, unit{i})); - eval(sprintf('Y%s = %s;', unit{i}, unit{i})); -end - -eval(sprintf('oldunit = %s;', old)); -eval(sprintf('newunit = %s;', new)); -if ~isequal(oldunit, newunit) - error('cannot convert %s to %s', old, new); -end - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% the following pertains to determining the scaling factor -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -% fundamental units -m = 1; -kg = 1; g = 0.001; % besides representing kg, also represent g to facilitate the processing further down -s = 1; -A = 1; -K = 1; -mol = 1; -cd = 1; - -% derived units -Hz = 1; -rad = 1; -sr = 1; -N = 1; -Pa = 1; -J = 1; -C = 1; -V = 1; -F = 1; -S = 1; -Wb = 1; -T = 1; -H = 1; -lm = 1; -lx = 1; -Bq = 1; -Gy = 1; -Sv = 1; -kat = 1; - -% deal with all possible prefixes -for i=1:length(unit) - eval(sprintf('d%s = 1e-1 * %s;', unit{i}, unit{i})); - eval(sprintf('c%s = 1e-2 * %s;', unit{i}, unit{i})); - eval(sprintf('m%s = 1e-3 * %s;', unit{i}, unit{i})); - eval(sprintf('u%s = 1e-6 * %s;', unit{i}, unit{i})); % note that u is used for micro - eval(sprintf('n%s = 1e-9 * %s;', unit{i}, unit{i})); - eval(sprintf('p%s = 1e-12 * %s;', unit{i}, unit{i})); - eval(sprintf('f%s = 1e-15 * %s;', unit{i}, unit{i})); - eval(sprintf('a%s = 1e-18 * %s;', unit{i}, unit{i})); - eval(sprintf('z%s = 1e-21 * %s;', unit{i}, unit{i})); - eval(sprintf('y%s = 1e-24 * %s;', unit{i}, unit{i})); - - eval(sprintf('da%s = 1e1 * %s;', unit{i}, unit{i})); - eval(sprintf('h%s = 1e2 * %s;', unit{i}, unit{i})); - eval(sprintf('k%s = 1e3 * %s;', unit{i}, unit{i})); - eval(sprintf('M%s = 1e6 * %s;', unit{i}, unit{i})); - eval(sprintf('G%s = 1e9 * %s;', unit{i}, unit{i})); - eval(sprintf('T%s = 1e12 * %s;', unit{i}, unit{i})); - eval(sprintf('P%s = 1e15 * %s;', unit{i}, unit{i})); - eval(sprintf('E%s = 1e18 * %s;', unit{i}, unit{i})); - eval(sprintf('Z%s = 1e21 * %s;', unit{i}, unit{i})); - eval(sprintf('Y%s = 1e24 * %s;', unit{i}, unit{i})); -end - -eval(sprintf('old2si = %s;', old)); -eval(sprintf('new2si = %s;', new)); - -factor = old2si/new2si; - -% remember the input args and the result, this will speed up the next call if the input is the same -previous_old{end+1} = old; -previous_new{end+1} = new; -previous_factor(end+1) = factor; diff --git a/external/fieldtrip/utilities/private/unparcellate.m b/external/fieldtrip/utilities/private/unparcellate.m index acb33771..b5b0e822 100755 --- a/external/fieldtrip/utilities/private/unparcellate.m +++ b/external/fieldtrip/utilities/private/unparcellate.m @@ -29,56 +29,78 @@ % If the input was bivariate data with a labelcmb, an optional second % output argument gives a list of the reference parcels. +% Undocumented input key-value pair: +% output = 'data' (default), or 'projection matrix', only works without 'labelcmb' in input + +output = ft_getopt(varargin, 'output', 'data'); + if isstruct(data) tmp = getsubfield(data, parameter); elseif size(parameter,2)==1 tmp = data; clear data; data.label = parameter; + data = setsubfield(data, parameter, tmp); elseif size(parameter,2)==2 % this contains labelcmb e.g. pairwise granger tmp = data; clear data; data.labelcmb = parameter; + data = setsubfield(data, parameter, tmp); end if isfield(data, 'label') % the data is chan_xxx or chan_chan_xxx - dimord = getdimord(data, parameter); - dimtok = tokenize(dimord, '_'); - dimsiz = getdimsiz(data, parameter); - % replace the number of parcels by the number of vertices in a parcel - dimsiz(strcmp(dimtok, 'chan')) = size(parcellation.pos,1); - - fun = nan(dimsiz); - - [parcelindx, chanindx] = match_str(parcellation.([parcelparam,'label']), data.label); - - if numel(dimtok)>1 && strcmp(dimtok{1}, 'chan') && strcmp(dimtok{2}, 'chan') - % chan_chan_xxx - for i=1:numel(parcelindx) - for j=1:numel(parcelindx) - p1 = parcellation.(parcelparam)==parcelindx(i); - p2 = parcellation.(parcelparam)==parcelindx(j); - c1 = chanindx(i); - c2 = chanindx(j); - fun(p1,p2,:) = repmat(tmp(c1,c2,:), [sum(p1) sum(p2) 1]); + switch output + case 'data' + dimord = getdimord(data, parameter); + dimtok = tokenize(dimord, '_'); + dimsiz = getdimsiz(data, parameter); + % replace the number of parcels by the number of vertices in a parcel + dimsiz(strcmp(dimtok, 'chan')) = size(parcellation.pos,1); + + fun = nan(dimsiz); + + [parcelindx, chanindx] = match_str(parcellation.([parcelparam,'label']), data.label); + + if strcmp(dimtok{1}, 'chan') && strcmp(dimtok{2}, 'chan') + % chan_chan_xxx + for i=1:numel(parcelindx) + for j=1:numel(parcelindx) + p1 = parcellation.(parcelparam)==parcelindx(i); + p2 = parcellation.(parcelparam)==parcelindx(j); + c1 = chanindx(i); + c2 = chanindx(j); + fun(p1,p2,:) = repmat(tmp(c1,c2,:), [sum(p1) sum(p2) 1]); + end + end + elseif strcmp(dimtok{1}, 'chan') + % chan_xxx + for i=1:numel(parcelindx) + p1 = parcellation.(parcelparam)==parcelindx(i); + c1 = chanindx(i); + fun(p1,:) = repmat(tmp(c1,:), [sum(p1) 1]); + end end - end - - elseif strcmp(dimtok{1}, 'chan') - % chan_xxx - for i=1:numel(parcelindx) - p1 = parcellation.(parcelparam)==parcelindx(i); - c1 = chanindx(i); - fun(p1,:) = repmat(tmp(c1,:), [sum(p1) 1]); - end - + varargout{1} = fun; + + case 'projection matrix' + [parcelindx, chanindx] = match_str(parcellation.([parcelparam,'label']), data.label); + ix = zeros(0,1); + iy = zeros(0,1); + for i=1:numel(parcelindx) + ix = cat(1,ix,find(parcellation.(parcelparam)==parcelindx(i))); + iy = cat(1,iy,ones(sum(parcellation.(parcelparam)==parcelindx(i)),1).*chanindx(i)); + end + fun = sparse(ix,iy,ones(numel(ix),1)); + + varargout{1} = fun; + varargout{2} = data.label(chanindx); + + otherwise end - varargout{1} = fun; - elseif isfield(data, 'labelcmb') % bivariate data diff --git a/external/fieldtrip/utilities/private/warning_once.m b/external/fieldtrip/utilities/private/warning_once.m deleted file mode 100644 index 9cede9d8..00000000 --- a/external/fieldtrip/utilities/private/warning_once.m +++ /dev/null @@ -1,235 +0,0 @@ -function [ws warned] = warning_once(varargin) -% -% WARNING_ONCE will throw a warning for every unique point in the -% stacktrace only, e.g. in a for-loop a warning is thrown only once. -% -% Use as one of the following -% warning_once(string) -% warning_once(id, string) -% Alternatively, you can use warning_once using a timeout -% warning_once(string, timeout) -% warning_once(id, string, timeout) -% where timeout should be inf if you don't want to see the warning ever -% again. -% -% Use as warning_once('-clear') to clear old warnings from the current -% stack -% -% It can be used instead of the MATLAB built-in function WARNING, thus as -% s = warning_once(...) -% or as -% warning_once(s) -% where s is a structure with fields 'identifier' and 'state', storing the -% state information. In other words, warning_once accepts as an input the -% same structure it returns as an output. This returns or restores the -% states of warnings to their previous values. -% -% It can also be used as -% [s w] = warning_once(...) -% where w is a boolean that indicates whether a warning as been thrown or not. -% -% Please note that you can NOT use it like this -% warning_once('the value is %d', 10) -% instead you should do -% warning_once(sprintf('the value is %d', 10)) - -% Copyright (C) 2012, Robert Oostenveld -% Copyright (C) 2013, Robert Oostenveld, J?rn M. Horschig -% -% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip -% for the documentation and details. -% -% FieldTrip is free software: you can redistribute it and/or modify -% it under the terms of the GNU General Public License as published by -% the Free Software Foundation, either version 3 of the License, or -% (at your option) any later version. -% -% FieldTrip is distributed in the hope that it will be useful, -% but WITHOUT ANY WARRANTY; without even the implied warranty of -% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -% GNU General Public License for more details. -% -% You should have received a copy of the GNU General Public License -% along with FieldTrip. If not, see . -% -% $Id: warning_once.m 10001 2014-12-02 09:04:27Z roboos $ - -global ft_default - -if nargin < 1 - error('You need to specify at least a warning message'); -end - -warned = false; -if isstruct(varargin{1}) - warning(varargin{1}); - return; -end - -if ~isfield(ft_default, 'warning') - ft_default.warning.stopwatch = []; - ft_default.warning.identifier = []; -end - -% put the arguments we will pass to warning() in this cell array -warningArgs = {}; - -if nargin==3 - % calling syntax (id, msg, timeout) - - warningArgs = varargin(1:2); - msg = warningArgs{2}; - timeout = varargin{3}; - fname = [warningArgs{1} '_' warningArgs{2}]; - -elseif nargin==2 && isnumeric(varargin{2}) - % calling syntax (msg, timeout) - - warningArgs = varargin(1); - msg = warningArgs{1}; - timeout = varargin{2}; - fname = warningArgs{1}; - -elseif nargin==2 && ~isnumeric(varargin{2}) - % calling syntax (id, msg) - - warningArgs = varargin(1:2); - msg = warningArgs{2}; - timeout = inf; - fname = [warningArgs{1} '_' warningArgs{2}]; - -elseif nargin==1 - % calling syntax (msg) - - warningArgs = varargin(1); - msg = warningArgs{1}; - timeout = inf; % default timeout in seconds - fname = [warningArgs{1}]; - -end - -if isempty(timeout) - error('Timeout ill-specified'); -end - -if timeout ~= inf - fname = decomma(fixname(fname)); % make a nice string that is allowed as structure fieldname - if length(fname) > 63 % MATLAB max name - fname = fname(1:63); - end - line = []; -else - % here, we create the fieldname functionA.functionB.functionC... - [tmpfname ft_default.warning.identifier line] = fieldnameFromStack(ft_default.warning.identifier); - if ~isempty(tmpfname) - fname = tmpfname; - clear tmpfname; - end -end - -if nargin==1 && ischar(varargin{1}) && strcmp('-clear', varargin{1}) - if strcmp(fname, '-clear') % reset all fields if called outside a function - ft_default.warning.identifier = []; - ft_default.warning.stopwatch = []; - else - if issubfield(ft_default.warning.identifier, fname) - ft_default.warning.identifier = rmsubfield(ft_default.warning.identifier, fname); - end - end - return; -end - -% and add the line number to make this unique for the last function -fname = horzcat(fname, line); - -if ~issubfield('ft_default.warning.stopwatch', fname) - ft_default.warning.stopwatch = setsubfield(ft_default.warning.stopwatch, fname, tic); -end - -now = toc(getsubfield(ft_default.warning.stopwatch, fname)); % measure time since first function call - -if ~issubfield(ft_default.warning.identifier, fname) || ... - (issubfield(ft_default.warning.identifier, fname) && now>getsubfield(ft_default.warning.identifier, [fname '.timeout'])) - - % create or reset field - ft_default.warning.identifier = setsubfield(ft_default.warning.identifier, fname, []); - - % warning never given before or timed out - ws = warning(warningArgs{:}); - ft_default.warning.identifier = setsubfield(ft_default.warning.identifier, [fname '.timeout'], now+timeout); - ft_default.warning.identifier = setsubfield(ft_default.warning.identifier, [fname '.ws'], msg); - warned = true; -else - - % the warning has been issued before, but has not timed out yet - ws = getsubfield(ft_default.warning.identifier, [fname '.ws']); - -end - -end % function warning_once - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -% helper functions -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -function name = decomma(name) -name(name==',')=[]; -end % function - -function [fname ft_previous_warnings line] = fieldnameFromStack(ft_previous_warnings) -% stack(1) is this function, stack(2) is warning_once -stack = dbstack('-completenames'); -if size(stack) < 3 - fname = []; - line = []; - return; -end -i0 = 3; -% ignore ft_preamble -while strfind(stack(i0).name, 'ft_preamble') - i0=i0+1; -end - -fname = horzcat(fixname(stack(end).name)); -if ~issubfield(ft_previous_warnings, fixname(stack(end).name)) - ft_previous_warnings.(fixname(stack(end).name)) = []; % iteratively build up structure fields -end - - -for i=numel(stack)-1:-1:(i0) - % skip postamble scripts - if strncmp(stack(i).name, 'ft_postamble', 12) - break; - end - - fname = horzcat(fname, '.', horzcat(fixname(stack(i).name))); % , stack(i).file - if ~issubfield(ft_previous_warnings, fname) % iteratively build up structure fields - setsubfield(ft_previous_warnings, fname, []); - end -end - -% line of last function call -line = ['.line', int2str(stack(i0).line)]; -end - -% function outcome = issubfield(strct, fname) -% substrindx = strfind(fname, '.'); -% if numel(substrindx) > 0 -% % separate the last fieldname from all former -% outcome = eval(['isfield(strct.' fname(1:substrindx(end)-1) ', ''' fname(substrindx(end)+1:end) ''')']); -% else -% % there is only one fieldname -% outcome = isfield(strct, fname); -% end -% end - -% function strct = rmsubfield(strct, fname) -% substrindx = strfind(fname, '.'); -% if numel(substrindx) > 0 -% % separate the last fieldname from all former -% strct = eval(['rmfield(strct.' fname(1:substrindx(end)-1) ', ''' fname(substrindx(end)+1:end) ''')']); -% else -% % there is only one fieldname -% strct = rmfield(strct, fname); -% end -% end diff --git a/external/fieldtrip/utilities/renamefields.m b/external/fieldtrip/utilities/renamefields.m new file mode 100644 index 00000000..84d2e5f0 --- /dev/null +++ b/external/fieldtrip/utilities/renamefields.m @@ -0,0 +1,56 @@ +function b = renamefields(a, old, new) + +% RENAMEFIELDS renames a selection of the fields in a structure +% +% Use as +% b = renamefields(a, old, new); +% which renames the fields with the old name to the new name. Fields that +% are specified but not present will be silently ignored. +% +% See also COPYFIELDS, KEEPFIELDS, REMOVEFIELDS + +% Copyright (C) 2014, Robert Oostenveld +% +% This file is part of FieldTrip, see http://www.ru.nl/neuroimaging/fieldtrip +% for the documentation and details. +% +% FieldTrip is free software: you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation, either version 3 of the License, or +% (at your option) any later version. +% +% FieldTrip is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with FieldTrip. If not, see . +% +% $Id: renamefields.m 11062 2016-01-15 11:27:50Z roboos $ + +if isempty(a) + % this prevents problems if a is an empty double, i.e. [] + return +end + +% these should be cell-arrays +if ischar(old) + old = {old}; +end +if ischar(new) + new = {new}; +end + +if length(old)~=length(new) + error('the number of field names does not match between old and new'); +end + +% keep the fields that were not mentioned +b = keepfields(a, setdiff(fieldnames(a), old)); +% copy the fields over with their new name +for i=1:length(old) + if isfield(a, old{i}); + b.(new{i}) = a.(old{i}); + end +end diff --git a/external/fieldtrip/utilities/tokenize.m b/external/fieldtrip/utilities/tokenize.m index 1824f56e..62cbce95 100644 --- a/external/fieldtrip/utilities/tokenize.m +++ b/external/fieldtrip/utilities/tokenize.m @@ -36,11 +36,13 @@ % You should have received a copy of the GNU General Public License % along with FieldTrip. If not, see . % -% $Id: tokenize.m 8771 2013-11-12 13:55:00Z roboos $ +% $Id: tokenize.m 10785 2015-10-17 09:41:47Z roboos $ % these are for remembering the type on subsequent calls with the same input arguments persistent previous_argin previous_argout +str = str(:)'; + if nargin<2 sep = [9:13 32]; % White space characters end @@ -56,9 +58,14 @@ return end -tok = {}; -f = find(ismember(str, sep)); +if numel(sep)==1 + f = find(str==sep); +else + f = find(ismember(str, sep)); +end f = [0, f, length(str)+1]; + +tok = cell(1, length(f)-1); for i=1:(length(f)-1) tok{i} = str((f(i)+1):(f(i+1)-1)); end diff --git a/help/index.html b/help/index.html index 980a58d7..3cad9d89 100644 --- a/help/index.html +++ b/help/index.html @@ -47,7 +47,7 @@

Welcome to SPM12

of the Licence, or (at your option) any later version.

diff --git a/man/biblio/methods_group.bib b/man/biblio/methods_group.bib index 48a6fe01..6201402b 100644 --- a/man/biblio/methods_group.bib +++ b/man/biblio/methods_group.bib @@ -3768,16 +3768,16 @@ @article{Pinotsis2013127 } @article{Pinotsis201139, -author = dp # and # karl, -title = {Neural fields, spectral responses and lateral connections}, -journal = ni, -volume = {55}, -number = {1}, -pages = {39--48}, -year = {2011}, -doi = {10.1016/j.neuroimage.2010.11.081}, -url = {http://www.sciencedirect.com/science/article/pii/S1053811910015594}, -keywords = {DCM} + author = dp # and # karl, + title = {Neural fields, spectral responses and lateral connections}, + journal = ni, + volume = {55}, + number = {1}, + pages = {39--48}, + year = {2011}, + doi = {10.1016/j.neuroimage.2010.11.081}, + url = {http://www.sciencedirect.com/science/article/pii/S1053811910015594}, + keywords = {DCM} } @PhDThesis{maria_phd, @@ -3803,117 +3803,117 @@ @Article{ashburner2013symmetric } @article{Bastos2012, -author = {A.M. Bastos} # {W.M. Usrey} # and # {R.A. Adams} # and # {G.R. Mangun} # and # {P. Fries} # and # karl, -title = {Canonical Microcircuits for Predictive Coding}, -journal = {Neuron}, -volume = {76}, -number = {4}, -pages = {695--711}, -year = {2012}, -doi = {10.1016/j.neuron.2012.10.038}, -url = {http://www.sciencedirect.com/science/article/pii/S0896627312009592} + author = {A.M. Bastos} # {W.M. Usrey} # and # {R.A. Adams} # and # {G.R. Mangun} # and # {P. Fries} # and # karl, + title = {Canonical Microcircuits for Predictive Coding}, + journal = {Neuron}, + volume = {76}, + number = {4}, + pages = {695--711}, + year = {2012}, + doi = {10.1016/j.neuron.2012.10.038}, + url = {http://www.sciencedirect.com/science/article/pii/S0896627312009592} } @article{Moran2011, -author = rm # and # klaas # and # rd # and # karl, -title = {Neural fields, spectral responses and lateral connections}, -journal = ni, -volume = {55}, -number = {4}, -pages = {1694--1708}, -year = {2011}, -doi = {10.1016/j.neuroimage.2011.01.012}, -url = {http://www.sciencedirect.com/science/article/pii/S1053811911000309} + author = rm # and # klaas # and # rd # and # karl, + title = {Neural fields, spectral responses and lateral connections}, + journal = ni, + volume = {55}, + number = {4}, + pages = {1694--1708}, + year = {2011}, + doi = {10.1016/j.neuroimage.2011.01.012}, + url = {http://www.sciencedirect.com/science/article/pii/S1053811911000309} } @article{Rigoux2014, -author = {L. Rigoux} # and # klaas # and # karl # and # jean, -title = {Bayesian model selection for group studies -- Revisited}, -journal = ni, -volume = {84}, -number = {}, -pages = {971--985}, -year = {2014}, -doi = {10.1016/j.neuroimage.2013.08.065}, -url = {http://www.sciencedirect.com/science/article/pii/S1053811913009300} + author = {L. Rigoux} # and # klaas # and # karl # and # jean, + title = {Bayesian model selection for group studies -- Revisited}, + journal = ni, + volume = {84}, + number = {}, + pages = {971--985}, + year = {2014}, + doi = {10.1016/j.neuroimage.2013.08.065}, + url = {http://www.sciencedirect.com/science/article/pii/S1053811913009300} } @article{rsDCM2014, -author = karl # and # {J. Kahan} # and # {B. Biswal} # and # {A. Razi}, -title = {A {DCM} for resting state f{MRI}}, -journal = ni, -volume = {}, -number = {}, -pages = {}, -year = {2014}, -doi = {10.1016/j.neuroimage.2013.12.009}, -url = {http://www.sciencedirect.com/science/article/pii/S1053811913012135} + author = karl # and # {J. Kahan} # and # {B. Biswal} # and # {A. Razi}, + title = {A {DCM} for resting state f{MRI}}, + journal = ni, + volume = {94}, + number = {}, + pages = {396--407}, + year = {2014}, + doi = {10.1016/j.neuroimage.2013.12.009}, + url = {http://www.sciencedirect.com/science/article/pii/S1053811913012135} } @article{Kahan2013, -author = {Joshua Kahan} # and # {Tom Foltynie}, -title = {Understanding DCM: Ten simple rules for the clinician}, -journal = ni, -volume = {83}, -number = {}, -pages = {542--549}, -year = {2013}, -doi = {10.1016/j.neuroimage.2013.07.008}, -url = {http://www.sciencedirect.com/science/article/pii/S105381191300760X}, -keywords = {DCM,connectivity}, -pdf = {/spm/doc/papers/Kahan_NeuroImage_DCM_2013.pdf} + author = {J. Kahan} # and # {T. Foltynie}, + title = {Understanding DCM: Ten simple rules for the clinician}, + journal = ni, + volume = {83}, + number = {}, + pages = {542--549}, + year = {2013}, + doi = {10.1016/j.neuroimage.2013.07.008}, + url = {http://www.sciencedirect.com/science/article/pii/S105381191300760X}, + keywords = {DCM,connectivity}, + pdf = {/spm/doc/papers/Kahan_NeuroImage_DCM_2013.pdf} } @article{CoCoMacHistory, -author = klaas, -title = {The history of CoCoMac}, -journal = ni, -volume = {80}, -number = {}, -pages = {46--52}, -year = {2013}, -doi = {10.1016/j.neuroimage.2013.03.016}, -url = {http://www.sciencedirect.com/science/article/pii/S1053811913002589}, -keywords = {connectivity} + author = klaas, + title = {The history of CoCoMac}, + journal = ni, + volume = {80}, + number = {}, + pages = {46--52}, + year = {2013}, + doi = {10.1016/j.neuroimage.2013.03.016}, + url = {http://www.sciencedirect.com/science/article/pii/S1053811913002589}, + keywords = {connectivity} } @article{Pinotsis2013, -author = dp # and # {Marco Leite} # and # karl, -title = {On conductance-based neural field models}, -journal = {Frontiers in Computational Neuroscience}, -volume = {7}, -number = {00158}, -pages = {}, -year = {2013}, -doi = {10.3389/fncom.2013.00158}, -url = {}, -keywords = {DCM} + author = dp # and # {Marco Leite} # and # karl, + title = {On conductance-based neural field models}, + journal = {Frontiers in Computational Neuroscience}, + volume = {7}, + number = {00158}, + pages = {}, + year = {2013}, + doi = {10.3389/fncom.2013.00158}, + url = {}, + keywords = {DCM} } @article{Pinotsis2014a, -author = dp # and # {N.Brunet} # and # {A. Bastos} # and # vl # and # {P.Fries} # and # karl, -title = {Contrast gain control and horizontal interactions in {V1}: A {DCM} study}, -journal = ni, -volume = {92}, -number = {}, -pages = {143--155}, -year = {2014}, -doi = {10.1016/j.neuroimage.2014.01.047}, -url = {}, -keywords = {DCM} + author = dp # and # {N.Brunet} # and # {A. Bastos} # and # vl # and # {P.Fries} # and # karl, + title = {Contrast gain control and horizontal interactions in {V1}: A {DCM} study}, + journal = ni, + volume = {92}, + number = {}, + pages = {143--155}, + year = {2014}, + doi = {10.1016/j.neuroimage.2014.01.047}, + url = {}, + keywords = {DCM} } @article{Moran2013, -author = rm # and # dp # and # karl, -title = {Neural Masses and Fields in Dynamic Causal Modelling}, -journal = {Frontiers in Computational Neuroscience}, -volume = {7}, -number = {00057}, -pages = {}, -year = {2013}, -doi = {10.3389/fncom.2013.00057}, -url = {}, -keywords = {DCM} + author = rm # and # dp # and # karl, + title = {Neural Masses and Fields in Dynamic Causal Modelling}, + journal = {Frontiers in Computational Neuroscience}, + volume = {7}, + number = {00057}, + pages = {}, + year = {2013}, + doi = {10.3389/fncom.2013.00057}, + url = {}, + keywords = {DCM} } @InProceedings{Maumet2014, @@ -3922,72 +3922,72 @@ @InProceedings{Maumet2014 booktitle = {20th Annual Meeting of the Organization for Human Brain Mapping}, location = {Hamburg}, year = {2014}, - keyword = {} + keywords = {} } @article{Friston2015, -author = karl # and # {A.M. Bastos} # and # dp # and # vl, -title = {LFP and oscillations - what do they tell us?}, -journal = {Current Opinion in Neurobiology}, -volume = {31}, -number = {}, -pages = {1--6}, -year = {2015}, -doi = {10.1016/j.conb.2014.05.004}, -url = {}, -keywords = {} + author = karl # and # {A.M. Bastos} # and # dp # and # vl, + title = {LFP and oscillations - what do they tell us?}, + journal = {Current Opinion in Neurobiology}, + volume = {31}, + number = {}, + pages = {1--6}, + year = {2015}, + doi = {10.1016/j.conb.2014.05.004}, + url = {}, + keywords = {} } @article{Pinotsis2014b, -author = dp # and # karl, -title = {Gamma oscillations and neural field DCMs can reveal cortical excitability and microstructure}, -journal = {AIMS Neuroscience Special Issue on Gamma Oscillations}, -volume = {1}, -number = {}, -pages = {4--24}, -year = {2014}, -doi = {10.3934/Neuroscience.2014.1.18}, -url = {}, -keywords = {} + author = dp # and # karl, + title = {Gamma oscillations and neural field DCMs can reveal cortical excitability and microstructure}, + journal = {AIMS Neuroscience Special Issue on Gamma Oscillations}, + volume = {1}, + number = {}, + pages = {4--24}, + year = {2014}, + doi = {10.3934/Neuroscience.2014.1.18}, + url = {}, + keywords = {} } @article{Pinotsis2014c, -author = dp # and # karl, -title = {Extracting novel information from neuroimaging data using neural fields}, -journal = {EPJ Nonlinear Biomedical Physics}, -volume = {2}, -number = {5}, -pages = {}, -year = {2014}, -doi = {10.1140/epjnbp18}, -url = {}, -keywords = {} + author = dp # and # karl, + title = {Extracting novel information from neuroimaging data using neural fields}, + journal = {EPJ Nonlinear Biomedical Physics}, + volume = {2}, + number = {5}, + pages = {}, + year = {2014}, + doi = {10.1140/epjnbp18}, + url = {}, + keywords = {} } @article{Pinotsis2014d, -author = dp # and # karl, -title = {Neural Fields, Masses and Bayesian Inference}, -journal = {Springer Mathematical Neuroscience Series}, -volume = {}, -number = {}, -pages = {}, -year = {2014}, -doi = {10.1007/978-3-642-54593-1_17}, -url = {}, -keywords = {} + author = dp # and # karl, + title = {Neural Fields, Masses and Bayesian Inference}, + journal = {Springer Mathematical Neuroscience Series}, + volume = {}, + number = {}, + pages = {}, + year = {2014}, + doi = {10.1007/978-3-642-54593-1_17}, + url = {}, + keywords = {} } @article{Wakeman2015, -author = {D.G. Wakeman} # and # rnah, -title = {A multi-subject, multi-modal human neuroimaging dataset}, -journal = {Scientific Data}, -volume = {2}, -number = {150001}, -pages = {}, -year = {2015}, -doi = {10.1038/sdata.2015.1}, -url = {http://www.nature.com/articles/sdata20151}, -keywords = {} + author = {D.G. Wakeman} # and # rnah, + title = {A multi-subject, multi-modal human neuroimaging dataset}, + journal = {Scientific Data}, + volume = {2}, + number = {150001}, + pages = {}, + year = {2015}, + doi = {10.1038/sdata.2015.1}, + url = {http://www.nature.com/articles/sdata20151}, + keywords = {} } @InCollection{Flandin2015, @@ -3998,7 +3998,33 @@ @InCollection{Flandin2015 publisher = {Academic Press}, pages = {495--500}, year = {2015}, - keyword = {FDR,RFT,topological}, + keywords = {FDR,RFT,topological}, doi = {10.1016/B978-0-12-397025-1.00322-5}, url = {http://www.sciencedirect.com/science/article/pii/B9780123970251003225} } + +@article{rsDCM2015, + author = {A. Razi} # and # {J. Kahan} # and # {G. Rees} # and # karl, + title = {Construct validation of {DCM} for resting state f{MRI}}, + journal = ni, + volume = {106}, + number = {}, + pages = {1--14}, + year = {2015}, + doi = {10.1016/j.neuroimage.2014.11.027}, + url = {http://www.sciencedirect.com/science/article/pii/S1053811914009446}, + keywords = {DCM} +} + +@article{stocDCM2011, + author = {Baojuan Li} # and # jean # and # klaas # and # will # and # {D. Hu} # and # karl, + title = {Generalised filtering and stochastic {DCM} for f{MRI}}, + journal = ni, + volume = {58}, + number = {2}, + pages = {442--457}, + year = {2011}, + doi = {10.1016/j.neuroimage.2011.01.085}, + url = {http://www.sciencedirect.com/science/article/pii/S1053811911001406}, + keywords = {DCM} +} diff --git a/man/dcm_rs/Fig1.png b/man/dcm_rs/Fig1.png new file mode 100644 index 00000000..ee19ff62 Binary files /dev/null and b/man/dcm_rs/Fig1.png differ diff --git a/man/dcm_rs/Fig2.png b/man/dcm_rs/Fig2.png new file mode 100644 index 00000000..5e2812e9 Binary files /dev/null and b/man/dcm_rs/Fig2.png differ diff --git a/man/dcm_rs/Fig3.png b/man/dcm_rs/Fig3.png new file mode 100644 index 00000000..f079cf0c Binary files /dev/null and b/man/dcm_rs/Fig3.png differ diff --git a/man/dcm_rs/dcm_mod_full.png b/man/dcm_rs/dcm_mod_full.png new file mode 100644 index 00000000..c873663a Binary files /dev/null and b/man/dcm_rs/dcm_mod_full.png differ diff --git a/man/dcm_rs/dcm_rs.tex b/man/dcm_rs/dcm_rs.tex new file mode 100644 index 00000000..f4a09a00 --- /dev/null +++ b/man/dcm_rs/dcm_rs.tex @@ -0,0 +1,190 @@ +\chapter{Dynamic Causal Modelling for resting state fMRI \label{Chap:DCM_rsfmri}} + +This chapter provides an extension to the framework of Dynamic Causal Modelling (DCM) for modelling intrinsic dynamics of a resting state network \cite{rsDCM2014,rsDCM2015}. This DCM estimates the effective connectivity among coupled populations of neurons, which subtends the observed functional connectivity in the frequency domain. We refer to this as spectral DCM (spDCM). + +\section{Theoretical background} +Spectral DCM uses a neuronally plausible power-law model of the coupled dynamics of neuronal populations to generate complex cross spectra among measured responses. Spectral DCM is distinct from stochastic DCM (sDCM) \cite{stocDCM2011} as it eschews the estimation of random fluctuations in (hidden) neural states; rendering spectral DCM essentially deterministic in nature. These models are similar to conventional deterministic DCM for fMRI \cite{dcm} but model endogenous activity that would reproduce the functional connectivity (correlations) observed in resting state fMRI. DCMs for resting state data are also slightly simpler; given that most resting state designs compare groups of subjects (e.g. patient cohorts vs. controls), spDCMs do not usually require the bilinear term (accounting for condition-specific effects on effective connection strengths). In other words, spectral DCM is intended to simply compare endogenous coupling between groups of subjects (e.g. patients vs. healthy controls). + +In modelling resting state activity, it is necessary to augment the ordinary differential equations used in standard DCM, with a stochastic term to model endogenous neuronal fluctuations. This renders the equations of the motion stochastic. The stochastic generative model for the resting state fMRI time series, like any other DCM, comprises of two equations: the Langevin form of evolution equation (motion) is written as: + +\begin{equation}\label{spdcm_eq1} +\dot{z}= f(z,u,\theta)+ v +\end{equation} +and the observation equation, which is a static nonlinear mapping from the hidden physiological states in Eq.~\ref{spdcm_eq1} to the observed BOLD activity and is written as: +\begin{equation}\label{spdcm_eq2} +y= h(z,u,\phi)+ e +\end{equation} +where $\dot{z}$ is the rate in change of the neuronal states $z$, $\theta$ are unknown parameters (i.e. the effective connectivity) and $v$ (resp. $e$) is the stochastic process -- called the state noise (resp. the measurement or observation noise) -- modelling the random neuronal fluctuations that drive the resting state activity. In the observation equations, $\phi$ are the unknown parameters of the (haemodynamic) observation function and $u$ represents any exogenous (or experimental) inputs -- that are usually absent in resting state designs. For resting state activity, Eq.~\ref{spdcm_eq1} takes on a very simple linear form: +\begin{equation}\label{spdcm_eq3} +\dot{z}= Az + Cu + v +\end{equation} +where $A$ is the Jacobian describing the behaviour -- i.e. the effective connectivity -- of the system near its stationary point ($f(z_{o})=0$) in the absence of the fluctuations $v$. It is to be noted that we can still include exogenous (or experimental) inputs, $u$ in our model. These inputs drive the hidden states -- and are usually set to zero in resting state models. It is perfectly possible to have external, (non-modulatory) stimuli, as in the case of conventional functional neuroimaging studies. For example, in \cite{dcm} we used an attention to visual motion paradigm to illustrate this point. + +Inverting the stochastic DCM of the form given by Eq.~\ref{spdcm_eq3} in the time domain, which includes state noise, is rather complicated because such models require estimation of not only the model parameters (and any hyperparameters that parameterise the random fluctuations), but also the hidden states, which become random (probabilistic) variables. Hence the unknown quantities to be estimated under a stochastic DCM are $\psi=\{z,\phi,\theta,\sigma\}$, where $\sigma$ refers to any hyperparameters (precisions or inverse covariances) defining the neuronal fluctuations. In terms of temporal characteristics, the hidden states are time-variant, whereas the model parameters (and hyperparameters) are time-invariant. There are various variational schemes in literature that can invert such models. For example, dynamic expectation maximization (DEM) \cite{karl_DEM} and generalized filtering (GF) \cite{karl_generalised_filtering}. + +Although the stochastic models in Eq.~\ref{spdcm_eq1} and their inversion in time domain provide a useful means to estimate effective connectivity they also require us to estimate hidden states. This poses a difficult inverse problem that is computationally demanding; especially when the number of hidden states becomes large. To finesse this problem, we furnish a constrained inversion of the stochastic model by parameterising the neuronal fluctuations. This parameterisation also provides an opportunity to compare parameters encoding the neuronal fluctuations among groups. The parameterisation of endogenous fluctuations means that the states are no longer probabilistic; hence the inversion scheme is significantly simpler, requiring estimation of only the parameters (and hyperparameters) of the model. + +Spectral DCM simply estimates the time-invariant parameters of their cross spectra. In other words, while stochastic DCMs model the observed BOLD timeseries of each node, spectral DCMs model the observed functional connectivity between nodes. Effectively, this is achieved by replacing the original timeseries with their second-order statistics (i.e., cross spectra), under stationarity assumptions. This means, instead of estimating time varying hidden states, we are estimating their covariance, which does not change with time. This means we need to estimate the covariance of the random fluctuations; where a scale free (power law) form for the state noise (resp. observation noise) that can be motivated from previous work on neuronal activity: +\begin{eqnarray}\label{spdcm_eq4} +& g_{v}(\omega,\theta) & = \alpha_{v}\omega^{-\beta_{v}}\\ \nonumber +& g_{e}(\omega,\theta) & = \alpha_{e}\omega^{-\beta_{e}} +\end{eqnarray} +Here, $\{\alpha,\beta\}\subset \theta$ are the parameters controlling the amplitudes and exponents of the spectral density of the neural fluctuations. This models neuronal noise with a generic $1/f^{\gamma}$ spectra, which characterizes fluctuations in systems that are at nonequilibrium steady-state. Using the model parameters, $\theta \supseteq \{A,C,\alpha, \beta\}$, we can simply generate the expected cross spectra: +\begin{eqnarray}\label{spdcm_eq5} +& y & = \kappa \ast v + e\\ \nonumber +& \kappa & = \partial_x h \mathrm{exp} (t \partial_x f ) \\ \nonumber +& g_{y}(\omega,\theta) & = |K(\omega)|^2 g_{v}(\omega,\theta) + g_{e}(\omega,\theta) +\end{eqnarray} +where $K(\omega)$ is the Fourier transform of the system's (first order) Volterra kernels $\kappa$, which are a function of the Jacobian or effective connectivity. The unknown quantities $\psi=\{\phi,\theta,\sigma\}$ of this deterministic model can now be estimated using standard Variational Laplace procedures \cite{karl_vb_laplace}. Here $g_{y} (\omega,\theta)$ represents the predicted cross spectra that can be estimated, for example, using autoregressive (AR) model. Specifically, we use a fourth-order autoregressive model to ensure smooth sample cross spectra of the sort predicted by the generative model. The frequencies usually considered for fMRI range from 1/128 Hz to 0.1 Hz in 32 evenly spaced frequency bins. + +\section{Practical example} + +Data used for this example can be downloaded from the SPM website. This dataset\footnote{Resting state DCM dataset: \url{http://www.fil.ion.ucl.ac.uk/spm/data/spDCM/}} consists of an exemplar subject from the full dataset available from the FC1000 project website\footnote{``1000 Functional Connectomes'' Project,: \url{http://fcon_1000.projects.nitrc.org/fcpClassic/FcpTable.html}} and was used in \cite{rsDCM2015} to interrogate the information integration in default mode network (DMN) -- a distinct brain system that is activated when an individual engages in introspection like mindwandering or daydreaming. The DMN comprises part of the medial prefrontal cortex (mPFC), posterior cingulate cortex (PCC) and parts of inferior parietal lobe and superior frontal regions. + +The archive contains the smoothed, spatially normalised, realigned, slice-time corrected images in the directory \texttt{func}. The directory \texttt{anat} contains a spatially normalised T1 structural image and the directory \texttt{GLM} contains the file \texttt{rp\_rest0000.txt} containing six head motion parameters. All preprocessing took place using SPM12. + +\subsection{Defining the GLM} +First, we need to set up the GLM analysis and extract our time series from the results. For resting state fMRI because there is no task so first we need to generate \texttt{SPM.mat} so that we can extract the time series. This can be done by following the steps below. + +Let's set up a batch that will specify the model and estimate it. + +\begin{enumerate} + \item The analysis directory you have downloaded should include: + \begin{enumerate} + \item A directory named \texttt{func}, which includes the preprocessed fMRI volumes. + \item A directory named \texttt{anat}, which includes a normalised T1 structural volume. + \item A directory named \texttt{GLM}, which include file \texttt{rp\_rest0000.txt} containing the movement regressors from the realignment step. + \end{enumerate} + \item In \matlab\ type +\begin{verbatim} +>> cd GLM +>> spm fmri +\end{verbatim} + \item From the main SPM window, click on the \textsc{Batch} button. + \item From the SPM menu at the top of the Batch Editor, select ``Stats $>$ fMRI model specification''. + \item Click \textsc{Directory} and choose the \texttt{GLM} directory that you made above. + \item \textsc{Units for design} [\textsc{scans}] + \item \textsc{Interscan interval} [2] + \item Click \textsc{Data \& Design}, Choose \textsc{New "Subject/Session"} + \item Click \textsc{Scans} and choose all the functional scans \texttt{swrestxxxx.img}. There should be 175 \texttt{*.img} files. + \item From the SPM menu at the top of the Batch Editor, select ``Stats $>$ model estimation''. + \item For \textsc{Select SPM.mat}, click on the \textsc{Dependency} button and choose the proposed item (the output from the previous module). + \item You should now be able to press the \textsc{Run} green arrow at the top of the Batch Editor window. This will specify and estimate the GLM. +\end{enumerate} + +We will also need to extract signal from CSF and white matter (WM) to be used as confound. Here is a step-by-step example for extracting the WM (Pons) time series which we will use as one of the nuisance variable: + +\begin{enumerate} + \item From the main SPM window, click on the \textsc{Batch} button. + \item From the SPM menu at the top of the Batch Editor, select ``Util $>$ Volume of interest'' + \item Select the \texttt{SPM.mat} file (generated during the previous section). + \item Adjust data: \texttt{NaN} + \item Which session: \texttt{1} + \item Name of VOI: \texttt{WM} + \item Select 'Region(s) of Interest' $>$ Sphere + \item Centre: \texttt{[0 -24 -33]} + \item VOI radius (mm): e.g.\texttt{6} mm + \item Select 'Movement of Centre' $>$ Fixed + \item Select 'Region of Interest' $>$ Mask Image + \item Image file: select \texttt{mask.nii} (in \texttt{GLM} folder) + \item Expression: \texttt{i1\&i2} + \item Now you should be able to press the green arrow button. This would extract the WM time series and save this as \texttt{VOI\_WM\_1.mat} in the working directory. +\end{enumerate} + +Do the same thing with to extract CSF (from one of the ventricles) signal with a sphere centred on \texttt{[0 -40 -5]}. This will create files \texttt{VOI\_CSF\_1.mat}. Next we need to adjust the SPM with the covariates. Do the following procedure: +\begin{enumerate} + \item From the main SPM window, click on the \textsc{Batch} button. + \item From the SPM menu at the top of the \textsc{Batch Editor}, select ``Stats $>$ fMRI model specification''. + \item Click Directory and choose the \texttt{GLM} directory that you made above. + \item \textsc{Units for design} [\textsc{scans}] + \item \textsc{Interscan interval} [2] + \item Click \textsc{Data \& Design}, Choose \textsc{New "Subject/Session"} + \item Click \textsc{Scans} and choose all the functional scans \texttt{swrestxxxx.img}. There should be 175 \texttt{*.img} files. + \item Click on Multiple Regressors. And then select the \texttt{VOI\_CSF\_1.mat}t, \texttt{VOI\_WM\_1.mat} and \texttt{rp\_rest000.txt}. Leave the rest of the fields as default. + \item From the SPM menu at the top of the Batch Editor, select ``Stats $>$ model estimation''. + \item For \textsc{Select SPM.mat}, click on the \textsc{Dependency} button and choose the proposed item (the output from the previous module). + \item You should now be able to press the \textsc{Run} green arrow at the top of the Batch Editor window (press 'continue' when asked for overwriting existing \textsc{SPM.mat} file). This will specify and estimate the GLM. +\end{enumerate} + +\subsection{Extracting time series} + +Once you have specified and estimated the GLM, here is now a step-by-step example for extracting the PCC time series: +\begin{enumerate} + \item From the main SPM window, click on the \textsc{Batch} button. + \item From the SPM menu at the top of the Batch Editor, select ``Util $>$ Volume of interest'' + \item Select the \texttt{SPM.mat} file (generated during the previous section). + \item Adjust data: \texttt{NaN} + \item Which session: \texttt{1} + \item Name of VOI: \texttt{PCC} + \item Select 'Region(s) of Interet' $>$ Sphere + \item Centre: \texttt{[0 -52 26]} + \item VOI radius(mm): e.g.\texttt{ 8} mm + \item Select 'Movement of Centre' $>$ Fixed + \item Select 'Region of Interest' $>$ Mask Image + \item Image file: select \texttt{mask.nii} (in \texttt{GLM} folder) + \item Expression: \texttt{i1\&i2} + \item Now you should be able to press the green arrow button. This would extract the WM time series and save this as \texttt{VOI\_CSF\_1.mat} in the working directory. +\end{enumerate} +SPM now computes the first principal component of the time series from all voxels included in the sphere. The result is stored (together with the original time series) in a file named \texttt{VOI\_PCC\_1.mat} in the working directory (the ``1'' refers to session 1). Do the same for the rest of the VOIs: mPFC (\texttt{[3 54 -2]}), LIPC ({[-50 -63 32]}) and RIPC (\texttt{[48 -69 35]}). + +\subsection{Specifying and estimating the DCM} + +Now we have extracted the time series, we are ready to build the DCM. We will look at a simplified version of the model described in \cite{rsDCM2015}. In our example here, we will model a fully connected system comprising PCC, mPFC and bilateral IPC. This DCM is shown schematically in Figure~\ref{spdcm_full}, and can be made as follows: +\begin{enumerate} + \item Press the large \texttt{Dynamic Causal Modelling} button. + \item Choose \textsc{specify}. + \item Select the \texttt{SPM.mat} file you just created when specifying the GLM. + \item \texttt{DCM\_???.mat}: e.g. \texttt{DMN}. + \item Select all VOIs in order \texttt{VOI\_PCC\_1}, \texttt{VOI\_mPFC\_1}, \texttt{VOI\_LIPC\_1} and \texttt{VOI\_RIPC\_1}. + \item Specify slice timings for each area. The default values are set to the last slice of the data, which was the default in the original DCM version. For sequential (as opposed to interleaved) data, this modelling option allows to use DCM in combination with any TR (slice timing differences). Here, we proceed with the default values. +\item Enter \texttt{0.04} for ``Echo Time, TE[s]''. +\item Define the fully connected model. Your connectivity matrix should look like the one in See Figure~\ref{spdcm_Fig1}. +\end{enumerate} +A polite ``Thank you'' completes the model specification process. A file called \texttt{DCM\_DMN.mat} will have been generated. + +You can now estimate the model parameters, either by pressing the DCM button again and choosing \textsc{estimate (cross-spectra)}, or by typing +\begin{verbatim} +>> spm_dcm_estimate('DCM_DMN'); +\end{verbatim} +from the \matlab\ command line. + +Once this is completed, you can review the results as follows: +\begin{enumerate} +\item Press the DCM button. +\item Choose \textsc{review}. +\item Select \texttt{DCM\_DMN.mat} +\end{enumerate} + +By clicking ``review...'' you can now select from multiple options, e.g. you can revisit the fit of the model (``Cross-spectra (BOLD)''), shown in Figure~\ref{spdcm_Fig2} or look at the parameter estimates for the endogenous coupling (``Coupling (A)'') as shown in Figure~\ref{spdcm_Fig3}. Of course, you can also explore the model results at the level of the \matlab\ command line by loading the model and inspecting the parameter estimates directly. These can be found in \texttt{DCM.Ep.A} (endogenous coupling) and \texttt{DCM.Ep.a }(neuronal parameters). + +\begin{figure}[ht] +\begin{center} +\includegraphics[width=100mm]{dcm_rs/dcm_mod_full} +\caption{\em DCM with fully connected model.\label{spdcm_full}} +\end{center} +\end{figure} + + +\begin{figure} +\begin{center} +\includegraphics[width=100mm]{dcm_rs/Fig1} +\caption{\em Specification of model depicted in Fig~\ref{spdcm_Fig1}. Filled circles define the structure of the extrinsic connections A such that all circles are filled since we are using a fully connected model here. +\label{spdcm_Fig1}} +\end{center} +\end{figure} + +\begin{figure}[ht] +\begin{center} +\includegraphics[width=140mm]{dcm_rs/Fig2} +\caption{\em Plot of predicted and observed cross spectral densities after convergence in Fig~\ref{spdcm_Fig2}. The dotted lines are measured cross spectra and solid lines are its predictions. The lower left panel shows the self cross spectra for the four regions. The rest of the graphics show both self and cross spectra for the four regions. +\label{spdcm_Fig2}} +\end{center} +\end{figure} + +\begin{figure}[ht] +\begin{center} +\includegraphics[width=140mm]{dcm_rs/Fig3} +\caption{\em This Fig~\ref{spdcm_Fig3} shows the estimated fixed A matrix (top and lower left panels). The posterior probabilities of these effective connectivity parameters are shown on the lower right panel. The red dashed line depicts the 95\% threshold. +\label{spdcm_Fig3}} +\end{center} +\end{figure} diff --git a/man/manual.pdf b/man/manual.pdf index e67dfc7c..0af62e17 100644 Binary files a/man/manual.pdf and b/man/manual.pdf differ diff --git a/man/manual.tex b/man/manual.tex index cf1b0a24..b3fe4a02 100644 --- a/man/manual.tex +++ b/man/manual.tex @@ -60,6 +60,7 @@ Vladimir Litvak \\ Rosalyn Moran \\ Will Penny \\ +Adeel Razi \\ Klaas Stephan \\ Peter Zeidman \\ \medskip @@ -165,6 +166,7 @@ \part{Data sets and examples} \include{dcm/dcm} \include{ppi/ppi} \include{bms/bms} +\include{dcm_rs/dcm_rs} \include{meg_sloc/meg_sloc} \include{mmn/mmn} \include{meeg_artefact/meeg_artefact} diff --git a/matlabbatch/cfg_basicio/cfg_cfg_basicio.m b/matlabbatch/cfg_basicio/cfg_cfg_basicio.m index aad1e84b..8e2a9042 100644 --- a/matlabbatch/cfg_basicio/cfg_cfg_basicio.m +++ b/matlabbatch/cfg_basicio/cfg_cfg_basicio.m @@ -4,7 +4,7 @@ % by MATLABBATCH using ConfGUI. It describes menu structure, validity % constraints and links to run time code. % Changes to this file will be overwritten if the ConfGUI batch is executed again. -% Created at 2013-10-02 13:55:50. +% Created at 2015-12-01 13:53:35. % --------------------------------------------------------------------- % files Files % --------------------------------------------------------------------- @@ -325,14 +325,38 @@ files.ufilter = '.*'; files.num = [0 Inf]; % --------------------------------------------------------------------- -% cfg_gzip_files GZip Files +% outdir Output directory +% --------------------------------------------------------------------- +outdir = cfg_files; +outdir.tag = 'outdir'; +outdir.name = 'Output directory'; +outdir.help = {'Output files will be placed in this folder. Leave empty to put them into the same folder as the original files.'}; +outdir.filter = {'dir'}; +outdir.ufilter = '.*'; +outdir.num = [0 1]; +% --------------------------------------------------------------------- +% keep Keep original files +% --------------------------------------------------------------------- +keep = cfg_menu; +keep.tag = 'keep'; +keep.name = 'Keep original files'; +keep.labels = { + 'Yes' + 'No' + }'; +keep.values = { + true + false + }'; +% --------------------------------------------------------------------- +% cfg_gzip_files Gzip Files % --------------------------------------------------------------------- cfg_gzip_files = cfg_exbranch; cfg_gzip_files.tag = 'cfg_gzip_files'; -cfg_gzip_files.name = 'GZip Files'; -cfg_gzip_files.val = {files }; -cfg_gzip_files.help = {'GZip each file in a set of files.'}; -cfg_gzip_files.prog = @(job)gzip(job.files); +cfg_gzip_files.name = 'Gzip Files'; +cfg_gzip_files.val = {files outdir keep }; +cfg_gzip_files.help = {'Gzip each file in a set of files.'}; +cfg_gzip_files.prog = @cfg_run_gzip_files; cfg_gzip_files.vout = @cfg_vout_gzip_files; % --------------------------------------------------------------------- % files File Set @@ -345,14 +369,38 @@ files.ufilter = '.*'; files.num = [0 Inf]; % --------------------------------------------------------------------- -% cfg_gunzip_files GunZip Files +% outdir Output directory +% --------------------------------------------------------------------- +outdir = cfg_files; +outdir.tag = 'outdir'; +outdir.name = 'Output directory'; +outdir.help = {'Output files will be placed in this folder. Leave empty to put them into the same folder as the original files.'}; +outdir.filter = {'dir'}; +outdir.ufilter = '.*'; +outdir.num = [0 1]; +% --------------------------------------------------------------------- +% keep Keep original files +% --------------------------------------------------------------------- +keep = cfg_menu; +keep.tag = 'keep'; +keep.name = 'Keep original files'; +keep.labels = { + 'Yes' + 'No' + }'; +keep.values = { + true + false + }'; +% --------------------------------------------------------------------- +% cfg_gunzip_files Gunzip Files % --------------------------------------------------------------------- cfg_gunzip_files = cfg_exbranch; cfg_gunzip_files.tag = 'cfg_gunzip_files'; -cfg_gunzip_files.name = 'GunZip Files'; -cfg_gunzip_files.val = {files }; -cfg_gunzip_files.help = {'GunZip each file in a set of files.'}; -cfg_gunzip_files.prog = @(job)gunzip(job.files); +cfg_gunzip_files.name = 'Gunzip Files'; +cfg_gunzip_files.val = {files outdir keep }; +cfg_gunzip_files.help = {'Gunzip each file in a set of files.'}; +cfg_gunzip_files.prog = @cfg_run_gunzip_files; cfg_gunzip_files.vout = @cfg_vout_gunzip_files; % --------------------------------------------------------------------- % name Input Name diff --git a/matlabbatch/cfg_basicio/cfg_cfg_basicio_def.m b/matlabbatch/cfg_basicio/cfg_cfg_basicio_def.m index ffd0e8cf..cdcdcbc7 100644 --- a/matlabbatch/cfg_basicio/cfg_cfg_basicio_def.m +++ b/matlabbatch/cfg_basicio/cfg_cfg_basicio_def.m @@ -5,7 +5,7 @@ % menu items and provides a full documentation of all fields that may % be present in a job variable for this application. % Changes to this file will be overwritten if the ConfGUI batch is executed again. -% Created at 2013-10-02 13:55:50. +% Created at 2015-12-01 13:53:35. cfg_basicio_def.file_dir.cfg_fileparts.files = ''; cfg_basicio_def.file_dir.dir_ops.cfg_cd.dir = ''; cfg_basicio_def.file_dir.dir_ops.cfg_mkdir.parent = ''; @@ -25,7 +25,11 @@ cfg_basicio_def.file_dir.file_ops.file_move.action.copyren.unique = ''; cfg_basicio_def.file_dir.file_ops.file_move.action.delete = false; cfg_basicio_def.file_dir.file_ops.cfg_gzip_files.files = ''; +cfg_basicio_def.file_dir.file_ops.cfg_gzip_files.outdir = {''}; +cfg_basicio_def.file_dir.file_ops.cfg_gzip_files.keep = true; cfg_basicio_def.file_dir.file_ops.cfg_gunzip_files.files = ''; +cfg_basicio_def.file_dir.file_ops.cfg_gunzip_files.outdir = {''}; +cfg_basicio_def.file_dir.file_ops.cfg_gunzip_files.keep = true; cfg_basicio_def.file_dir.file_ops.cfg_named_file.name = ''; cfg_basicio_def.file_dir.file_ops.cfg_named_file.files = {''}; cfg_basicio_def.file_dir.file_ops.file_fplist.dir = ''; diff --git a/matlabbatch/cfg_basicio/cfg_run_file_filter.m b/matlabbatch/cfg_basicio/cfg_run_file_filter.m index 97a907bf..1b598dea 100644 --- a/matlabbatch/cfg_basicio/cfg_run_file_filter.m +++ b/matlabbatch/cfg_basicio/cfg_run_file_filter.m @@ -9,8 +9,8 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_run_file_filter.m 1716 2008-05-23 08:18:45Z volkmar $ +% $Id: cfg_run_file_filter.m 6573 2015-10-15 13:18:29Z volkmar $ -rev = '$Rev: 1716 $'; %#ok +rev = '$Rev: 6573 $'; %#ok -out.files = cfg_getfile('filter', job.files, job.typ, job.filter, job.frames); +out.files = cfg_getfile('filter', job.files, job.typ, job.filter); diff --git a/matlabbatch/cfg_basicio/cfg_run_gunzip_files.m b/matlabbatch/cfg_basicio/cfg_run_gunzip_files.m new file mode 100644 index 00000000..13b69fb0 --- /dev/null +++ b/matlabbatch/cfg_basicio/cfg_run_gunzip_files.m @@ -0,0 +1,22 @@ +function out = cfg_run_gunzip_files(job) +% Run gunzip on a list of files. +% +% This code is part of a batch job configuration system for MATLAB. See +% help matlabbatch +% for a general overview. +%_______________________________________________________________________ +% Copyright (C) 2007 Freiburg Brain Imaging + +% Volkmar Glauche +% $Id: cfg_run_gunzip_files.m 6628 2015-12-04 08:20:51Z volkmar $ + +rev = '$Rev: 6628 $'; %#ok + +if isempty(job.outdir) || isempty(job.outdir{1}) + out = gunzip(job.files); +else + out = gunzip(job.files, job.outdir{1}); +end +if ~job.keep + delete(job.files{:}); +end \ No newline at end of file diff --git a/matlabbatch/cfg_basicio/cfg_run_gzip_files.m b/matlabbatch/cfg_basicio/cfg_run_gzip_files.m new file mode 100644 index 00000000..769ecb77 --- /dev/null +++ b/matlabbatch/cfg_basicio/cfg_run_gzip_files.m @@ -0,0 +1,22 @@ +function out = cfg_run_gzip_files(job) +% Run gzip on a list of files. +% +% This code is part of a batch job configuration system for MATLAB. See +% help matlabbatch +% for a general overview. +%_______________________________________________________________________ +% Copyright (C) 2007 Freiburg Brain Imaging + +% Volkmar Glauche +% $Id: cfg_run_gzip_files.m 6628 2015-12-04 08:20:51Z volkmar $ + +rev = '$Rev: 6628 $'; %#ok + +if isempty(job.outdir) || isempty(job.outdir{1}) + out = gzip(job.files); +else + out = gzip(job.files, job.outdir{1}); +end +if ~job.keep + delete(job.files{:}); +end \ No newline at end of file diff --git a/matlabbatch/cfg_basicio/cfg_run_runjobs.m b/matlabbatch/cfg_basicio/cfg_run_runjobs.m index 0f954afe..d14f2047 100644 --- a/matlabbatch/cfg_basicio/cfg_run_runjobs.m +++ b/matlabbatch/cfg_basicio/cfg_run_runjobs.m @@ -12,13 +12,13 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_run_runjobs.m 5678 2013-10-11 14:58:04Z volkmar $ +% $Id: cfg_run_runjobs.m 6514 2015-08-06 10:07:53Z volkmar $ -rev = '$Rev: 5678 $'; %#ok +rev = '$Rev: 6514 $'; %#ok if isfield(job.save, 'savejobs') [p, n, e] = fileparts(job.save.savejobs.outstub); - outfmt = fullfile(job.save.savejobs.outdir{1}, sprintf('%s_%%0%dd.m', n, ceil(log10(numel(job.inputs))+1))); + outfmt = strrep(fullfile(job.save.savejobs.outdir{1}, sprintf('%s_%%0%dd.m', n, ceil(log10(numel(job.inputs))+1))), '\', '\\'); end; hjobs = cell(size(job.inputs)); sts = false(size(job.inputs)); diff --git a/matlabbatch/cfg_basicio/cfg_vout_gunzip_files.m b/matlabbatch/cfg_basicio/cfg_vout_gunzip_files.m index 908d9dac..9a1f8395 100644 --- a/matlabbatch/cfg_basicio/cfg_vout_gunzip_files.m +++ b/matlabbatch/cfg_basicio/cfg_vout_gunzip_files.m @@ -1,9 +1,7 @@ function dep = cfg_vout_gunzip_files(job) -% Define virtual outputs for "GZip Files". File names can either be +% Define virtual outputs for "Gunzip Files". File names can either be % assigned to a cfg_files input or to a evaluated cfg_entry. -% Note that there is no cfg_run_gzip_files.m. The .prog callback is calling -% MATLAB gzip directly. % % This code is part of a batch job configuration system for MATLAB. See % help matlabbatch @@ -12,11 +10,11 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_vout_gunzip_files.m 5685 2013-10-11 14:58:24Z volkmar $ +% $Id: cfg_vout_gunzip_files.m 6627 2015-12-04 08:20:49Z volkmar $ -rev = '$Rev: 5685 $'; %#ok +rev = '$Rev: 6627 $'; %#ok dep = cfg_dep; -dep.sname = 'GunZipped Files'; +dep.sname = 'Gunzipped Files'; dep.src_output = substruct('()',{':'}); dep.tgt_spec = cfg_findspec({{'class','cfg_files', 'strtype','e'}}); diff --git a/matlabbatch/cfg_basicio/cfg_vout_gzip_files.m b/matlabbatch/cfg_basicio/cfg_vout_gzip_files.m index 1f2a78ad..07e39da2 100644 --- a/matlabbatch/cfg_basicio/cfg_vout_gzip_files.m +++ b/matlabbatch/cfg_basicio/cfg_vout_gzip_files.m @@ -1,9 +1,7 @@ function dep = cfg_vout_gzip_files(job) -% Define virtual outputs for "GZip Files". File names can either be +% Define virtual outputs for "Gzip Files". File names can either be % assigned to a cfg_files input or to a evaluated cfg_entry. -% Note that there is no cfg_run_gzip_files.m. The .prog callback is calling -% MATLAB gzip directly. % % This code is part of a batch job configuration system for MATLAB. See % help matlabbatch @@ -12,11 +10,11 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_vout_gzip_files.m 5685 2013-10-11 14:58:24Z volkmar $ +% $Id: cfg_vout_gzip_files.m 6627 2015-12-04 08:20:49Z volkmar $ -rev = '$Rev: 5685 $'; %#ok +rev = '$Rev: 6627 $'; %#ok dep = cfg_dep; -dep.sname = 'GZipped Files'; +dep.sname = 'Gzipped Files'; dep.src_output = substruct('()',{':'}); dep.tgt_spec = cfg_findspec({{'class','cfg_files', 'strtype','e'}}); diff --git a/matlabbatch/cfg_basicio/src/01_file_dir_ops/02_file_ops/batch_basicio_02_gzip_files.m b/matlabbatch/cfg_basicio/src/01_file_dir_ops/02_file_ops/batch_basicio_02_gzip_files.m index fe9f0bad..e078c6ed 100644 --- a/matlabbatch/cfg_basicio/src/01_file_dir_ops/02_file_ops/batch_basicio_02_gzip_files.m +++ b/matlabbatch/cfg_basicio/src/01_file_dir_ops/02_file_ops/batch_basicio_02_gzip_files.m @@ -1,13 +1,13 @@ %----------------------------------------------------------------------- -% Job saved on 25-Sep-2013 15:20:44 by cfg_util (rev $Rev: 5685 $) -% spm SPM - SPM12b (beta) +% Job saved on 01-Dec-2015 13:23:09 by cfg_util (rev $Rev: 6627 $) +% spm SPM - SPM12 (12.1) % cfg_basicio BasicIO - Unknown -% cfg_logextract LogExt - Unknown -% tbx_dpc dPC Tools - Unknown -% perfusion Perfusion - Unknown % dtijobs DTI tools - Unknown % impexp_NiftiMrStruct NiftiMrStruct - Unknown +% cfg_logextract LogExt - Unknown +% tbx_dpc dPC Tools - Unknown % prt PRoNTo - Unknown +% perfusion Perfusion - Unknown % menu_cfg ConfGUI - Unknown %----------------------------------------------------------------------- matlabbatch{1}.menu_cfg.menu_entry.conf_files.type = 'cfg_files'; @@ -18,13 +18,43 @@ matlabbatch{1}.menu_cfg.menu_entry.conf_files.dir = ''; matlabbatch{1}.menu_cfg.menu_entry.conf_files.num = [0 Inf]; matlabbatch{1}.menu_cfg.menu_entry.conf_files.check = []; +matlabbatch{1}.menu_cfg.menu_entry.conf_files.rewrite_job = []; matlabbatch{1}.menu_cfg.menu_entry.conf_files.help = {'Select a set of files.'}; matlabbatch{1}.menu_cfg.menu_entry.conf_files.def = []; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.type = 'cfg_exbranch'; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.name = 'GZip Files'; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.tag = 'cfg_gzip_files'; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.val{1}(1) = cfg_dep('Files: File Set (cfg_files)', substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1})); -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.prog = @(job)gzip(job.files); -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.vout = @cfg_vout_gzip_files; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.check = []; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.help = {'GZip each file in a set of files.'}; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.type = 'cfg_files'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.name = 'Output directory'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.tag = 'outdir'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.filter = 'dir'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.ufilter = '.*'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.dir = []; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.num = [0 1]; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.check = []; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.rewrite_job = []; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.help = {'Output files will be placed in this folder. Leave empty to put them into the same folder as the original files.'}; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.def = []; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.type = 'cfg_menu'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.name = 'Keep original files'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.tag = 'keep'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.labels = { + 'Yes' + 'No' + }'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.values = { + true + false + }'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.check = []; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.rewrite_job = []; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.help = {}; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.def = []; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.type = 'cfg_exbranch'; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.name = 'Gzip Files'; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.tag = 'cfg_gzip_files'; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{1}(1) = cfg_dep('Files: File Set (cfg_files)', substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1})); +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{2}(1) = cfg_dep('Files: Output directory (cfg_files)', substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1})); +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{3}(1) = cfg_dep('Menu: Keep original files (cfg_menu)', substruct('.','val', '{}',{3}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1})); +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.prog = @cfg_run_gzip_files; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.vout = @cfg_vout_gzip_files; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.check = []; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.rewrite_job = []; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.help = {'Gzip each file in a set of files.'}; diff --git a/matlabbatch/cfg_basicio/src/01_file_dir_ops/02_file_ops/batch_basicio_03_gunzip_files.m b/matlabbatch/cfg_basicio/src/01_file_dir_ops/02_file_ops/batch_basicio_03_gunzip_files.m index 65669cfc..5395e71a 100644 --- a/matlabbatch/cfg_basicio/src/01_file_dir_ops/02_file_ops/batch_basicio_03_gunzip_files.m +++ b/matlabbatch/cfg_basicio/src/01_file_dir_ops/02_file_ops/batch_basicio_03_gunzip_files.m @@ -1,13 +1,13 @@ %----------------------------------------------------------------------- -% Job saved on 25-Sep-2013 15:22:57 by cfg_util (rev $Rev: 5685 $) -% spm SPM - SPM12b (beta) +% Job saved on 01-Dec-2015 13:23:09 by cfg_util (rev $Rev: 6627 $) +% spm SPM - SPM12 (12.1) % cfg_basicio BasicIO - Unknown -% cfg_logextract LogExt - Unknown -% tbx_dpc dPC Tools - Unknown -% perfusion Perfusion - Unknown % dtijobs DTI tools - Unknown % impexp_NiftiMrStruct NiftiMrStruct - Unknown +% cfg_logextract LogExt - Unknown +% tbx_dpc dPC Tools - Unknown % prt PRoNTo - Unknown +% perfusion Perfusion - Unknown % menu_cfg ConfGUI - Unknown %----------------------------------------------------------------------- matlabbatch{1}.menu_cfg.menu_entry.conf_files.type = 'cfg_files'; @@ -18,13 +18,43 @@ matlabbatch{1}.menu_cfg.menu_entry.conf_files.dir = ''; matlabbatch{1}.menu_cfg.menu_entry.conf_files.num = [0 Inf]; matlabbatch{1}.menu_cfg.menu_entry.conf_files.check = []; +matlabbatch{1}.menu_cfg.menu_entry.conf_files.rewrite_job = []; matlabbatch{1}.menu_cfg.menu_entry.conf_files.help = {'Select a set of files.'}; matlabbatch{1}.menu_cfg.menu_entry.conf_files.def = []; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.type = 'cfg_exbranch'; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.name = 'GunZip Files'; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.tag = 'cfg_gunzip_files'; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.val{1}(1) = cfg_dep('Files: File Set (cfg_files)', substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1})); -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.prog = @(job)gunzip(job.files); -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.vout = @cfg_vout_gunzip_files; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.check = []; -matlabbatch{2}.menu_cfg.menu_struct.conf_exbranch.help = {'GunZip each file in a set of files.'}; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.type = 'cfg_files'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.name = 'Output directory'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.tag = 'outdir'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.filter = 'dir'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.ufilter = '.*'; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.dir = []; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.num = [0 1]; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.check = []; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.rewrite_job = []; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.help = {'Output files will be placed in this folder. Leave empty to put them into the same folder as the original files.'}; +matlabbatch{2}.menu_cfg.menu_entry.conf_files.def = []; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.type = 'cfg_menu'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.name = 'Keep original files'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.tag = 'keep'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.labels = { + 'Yes' + 'No' + }'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.values = { + true + false + }'; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.check = []; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.rewrite_job = []; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.help = {}; +matlabbatch{3}.menu_cfg.menu_entry.conf_menu.def = []; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.type = 'cfg_exbranch'; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.name = 'Gunzip Files'; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.tag = 'cfg_gunzip_files'; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{1}(1) = cfg_dep('Files: File Set (cfg_files)', substruct('.','val', '{}',{1}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1})); +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{2}(1) = cfg_dep('Files: Output directory (cfg_files)', substruct('.','val', '{}',{2}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1})); +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.val{3}(1) = cfg_dep('Menu: Keep original files (cfg_menu)', substruct('.','val', '{}',{3}, '.','val', '{}',{1}, '.','val', '{}',{1}), substruct('()',{1})); +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.prog = @cfg_run_gunzip_files; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.vout = @cfg_vout_gunzip_files; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.check = []; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.rewrite_job = []; +matlabbatch{4}.menu_cfg.menu_struct.conf_exbranch.help = {'Gunzip each file in a set of files.'}; diff --git a/matlabbatch/cfg_basicio/src/create_cfg_cfg_basicio.m b/matlabbatch/cfg_basicio/src/create_cfg_cfg_basicio.m index fa333080..3583518c 100644 --- a/matlabbatch/cfg_basicio/src/create_cfg_cfg_basicio.m +++ b/matlabbatch/cfg_basicio/src/create_cfg_cfg_basicio.m @@ -10,7 +10,7 @@ cfg_util('deljob',id); function out = process_dir(d) -[m sd] = cfg_getfile('fplist', d,'^batch_basicio_[0-9]*_.*\.m$'); +[m, sd] = cfg_getfile('fplist', d,'^batch_basicio_[0-9]*_.*\.m$'); %% List of modules % Each module batch is assumed to contain exactly one cfg_exbranch, and % this should be the last output of the batch @@ -23,13 +23,13 @@ cfg_util('deljob',id); end %% List of subdirs -[u n e] = cellfun(@fileparts,sd,'UniformOutput',false); +[u, n, e] = cellfun(@fileparts,sd,'UniformOutput',false); sd = sd(cellfun(@isempty,regexp(strcat(n,e),'^\.'))); outs = cell(size(sd)); for cs = 1:numel(sd) outs{cs} = process_dir(sd{cs}); end -outa = {outm{:} outs{:}}; +outa = [outm(:); outs(:)]; %% Top level batch % This batch needs to be modified if the number of modules or sublevels % changes - the top level choice will have to contain the correct number of diff --git a/matlabbatch/cfg_ui.m b/matlabbatch/cfg_ui.m index 2835711b..9f13238a 100644 --- a/matlabbatch/cfg_ui.m +++ b/matlabbatch/cfg_ui.m @@ -27,9 +27,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_ui.m 6463 2015-05-29 14:10:15Z volkmar $ +% $Id: cfg_ui.m 6515 2015-08-06 10:07:55Z volkmar $ -rev = '$Rev: 6463 $'; %#ok +rev = '$Rev: 6515 $'; %#ok % edit the above text to modify the response to help cfg_ui @@ -703,7 +703,7 @@ function MenuFileRun_Callback(hObject, eventdata, handles) cfg_util('run',udmodlist(1).cjob); catch le = lasterror; - if strcmpi(questdlg(sprintf('%s\n\nSave error information?', le.message),'Error in job execution', 'Yes','No','Yes'), 'yes') + if strcmpi(questdlg(sprintf('An error occured during job execution. Please see the MATLAB command window for details.\n\nSave error information?'),'Error in job execution', 'Yes','No','Yes'), 'yes') opwd = pwd; if ~isempty(udmodlist.wd) cd(udmodlist.wd); diff --git a/matlabbatch/gencode_rvalue.m b/matlabbatch/gencode_rvalue.m index 17067f8d..af704ff0 100644 --- a/matlabbatch/gencode_rvalue.m +++ b/matlabbatch/gencode_rvalue.m @@ -28,9 +28,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: gencode_rvalue.m 6109 2014-07-17 11:37:40Z volkmar $ +% $Id: gencode_rvalue.m 6640 2015-12-11 10:17:09Z volkmar $ -rev = '$Rev: 6109 $'; %#ok +rev = '$Rev: 6640 $'; %#ok if nargin < 2 cflag = false; @@ -169,6 +169,20 @@ % generation. str = strrep(stritem, '''', ''''''); for k = 1:numel(str) - str{k} = sprintf('''%s''', str{k}); + if ~any(str{k} == char(0)) && ~any(str{k} == char(9)) && ~any(str{k} == char(10)) + str{k} = sprintf('''%s''', str{k}); + else + % first, quote sprintf special chars % and \ + % second, replace special characters by sprintf equivalents + replacements = {'%', '%%'; ... + '\', '\\'; ... + char(0), '\0'; ... + char(9), '\t'; ... + char(10), '\n'}; + for cr = 1:size(replacements, 1) + str{k} = strrep(str{k}, replacements{cr,:}); + end + str{k} = sprintf('sprintf(''%s'')', str{k}); + end end diff --git a/matlabbatch/private/cfg_disp_error.m b/matlabbatch/private/cfg_disp_error.m index b0e83aad..9fe0faf9 100644 --- a/matlabbatch/private/cfg_disp_error.m +++ b/matlabbatch/private/cfg_disp_error.m @@ -11,9 +11,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_disp_error.m 1862 2008-06-30 14:12:49Z volkmar $ +% $Id: cfg_disp_error.m 6574 2015-10-15 13:18:30Z volkmar $ -rev = '$Rev: 1862 $'; %#ok +rev = '$Rev: 6574 $'; %#ok if isfield(l,'stack'), % Does not always exist estr = cell(numel(l.stack)+1,1); @@ -32,8 +32,13 @@ catch id = ''; end - estr{m+1} = sprintf('In file "%s"%s, function "%s" at line %d.', ... - l.stack(m).file, id, l.stack(m).name, l.stack(m).line); + if usejava('desktop') + estr{m+1} = sprintf('In file "%s"%s, function "%s" at line %d.', ... + l.stack(m).file, id, l.stack(m).name, l.stack(m).file, l.stack(m).line, l.stack(m).line); + else + estr{m+1} = sprintf('In file "%s"%s, function "%s" at line %d.', ... + l.stack(m).file, id, l.stack(m).name, l.stack(m).line); + end end end estr{1} = l.message; diff --git a/matlabbatch/private/cfg_mlbatch_defaults.m b/matlabbatch/private/cfg_mlbatch_defaults.m index 95654b39..fc7a166a 100644 --- a/matlabbatch/private/cfg_mlbatch_defaults.m +++ b/matlabbatch/private/cfg_mlbatch_defaults.m @@ -11,9 +11,9 @@ % Copyright (C) 2007 Freiburg Brain Imaging % Volkmar Glauche -% $Id: cfg_mlbatch_defaults.m 6463 2015-05-29 14:10:15Z volkmar $ +% $Id: cfg_mlbatch_defaults.m 6641 2015-12-11 10:17:11Z volkmar $ -rev = '$Rev: 6463 $'; %#ok +rev = '$Rev: 6641 $'; %#ok try % Font definition for cfg_ui user interface @@ -103,35 +103,32 @@ cfg_defaults.msgtpl( 4).identifier = '^matlabbatch:deprecated'; cfg_defaults.msgtpl( 4).destination = 'none'; cfg_defaults.msgtpl( 5) = cfg_defaults.msgdef; -cfg_defaults.msgtpl( 5).identifier = '^MATLAB:nargchk'; +cfg_defaults.msgtpl( 5).identifier = '^matlabbatch:usage'; cfg_defaults.msgtpl( 5).level = 'error'; cfg_defaults.msgtpl( 6) = cfg_defaults.msgdef; -cfg_defaults.msgtpl( 6).identifier = '^matlabbatch:usage'; -cfg_defaults.msgtpl( 6).level = 'error'; +cfg_defaults.msgtpl( 6).identifier = '^matlabbatch:setval'; +cfg_defaults.msgtpl( 6).destination = 'none'; cfg_defaults.msgtpl( 7) = cfg_defaults.msgdef; -cfg_defaults.msgtpl( 7).identifier = '^matlabbatch:setval'; -cfg_defaults.msgtpl( 7).destination = 'none'; +cfg_defaults.msgtpl( 7).identifier = '^matlabbatch:run:nomods'; +cfg_defaults.msgtpl( 7).level = 'info'; cfg_defaults.msgtpl( 8) = cfg_defaults.msgdef; -cfg_defaults.msgtpl( 8).identifier = '^matlabbatch:run:nomods'; -cfg_defaults.msgtpl( 8).level = 'info'; +cfg_defaults.msgtpl( 8).identifier = '^matlabbatch:cfg_struct2cfg'; +cfg_defaults.msgtpl( 8).destination = 'none'; cfg_defaults.msgtpl( 9) = cfg_defaults.msgdef; -cfg_defaults.msgtpl( 9).identifier = '^matlabbatch:cfg_struct2cfg'; -cfg_defaults.msgtpl( 9).destination = 'none'; +cfg_defaults.msgtpl( 9).identifier = '^MATLAB:inputdlg'; +cfg_defaults.msgtpl( 9).level = 'error'; cfg_defaults.msgtpl(10) = cfg_defaults.msgdef; -cfg_defaults.msgtpl(10).identifier = '^MATLAB:inputdlg'; +cfg_defaults.msgtpl(10).identifier = '^MATLAB:listdlg'; cfg_defaults.msgtpl(10).level = 'error'; cfg_defaults.msgtpl(11) = cfg_defaults.msgdef; -cfg_defaults.msgtpl(11).identifier = '^MATLAB:listdlg'; +cfg_defaults.msgtpl(11).identifier = '^MATLAB:num2str'; cfg_defaults.msgtpl(11).level = 'error'; cfg_defaults.msgtpl(12) = cfg_defaults.msgdef; -cfg_defaults.msgtpl(12).identifier = '^MATLAB:num2str'; -cfg_defaults.msgtpl(12).level = 'error'; +cfg_defaults.msgtpl(12).identifier = '^matlabbatch:ok_subsasgn'; +cfg_defaults.msgtpl(12).destination = 'none'; cfg_defaults.msgtpl(13) = cfg_defaults.msgdef; -cfg_defaults.msgtpl(13).identifier = '^matlabbatch:ok_subsasgn'; +cfg_defaults.msgtpl(13).identifier = 'matlabbatch:checkval:numcheck:transposed'; cfg_defaults.msgtpl(13).destination = 'none'; -cfg_defaults.msgtpl(14) = cfg_defaults.msgdef; -cfg_defaults.msgtpl(14).identifier = 'matlabbatch:checkval:numcheck:transposed'; -cfg_defaults.msgtpl(14).destination = 'none'; % value check for cfg_branch/choice/repeat items - set to false after % configuration has been initialised to speed up job diff --git a/matlabbatch/private/inputdlg.m b/matlabbatch/private/inputdlg.m index a728f13d..1cfe0049 100644 --- a/matlabbatch/private/inputdlg.m +++ b/matlabbatch/private/inputdlg.m @@ -47,13 +47,7 @@ % QUESTDLG, TEXTWRAP, UIWAIT, WARNDLG . % Copyright 1994-2007 The MathWorks, Inc. -% $Revision: 6463 $ - -%%%%%%%%%%%%%%%%%%%% -%%% Nargin Check %%% -%%%%%%%%%%%%%%%%%%%% -cfg_message(nargchk(0,5,nargin,'struct')); -cfg_message(nargoutchk(0,1,nargout,'struct')); +% $Revision: 6641 $ %%%%%%%%%%%%%%%%%%%%%%%%% %%% Handle Input Args %%% diff --git a/matlabbatch/private/listdlg.m b/matlabbatch/private/listdlg.m index 1f8bbc60..486ebff8 100644 --- a/matlabbatch/private/listdlg.m +++ b/matlabbatch/private/listdlg.m @@ -42,7 +42,7 @@ % MSGBOX, QUESTDLG, WARNDLG. % Copyright 1984-2005 The MathWorks, Inc. -% $Revision: 6463 $ $Date: 2005/10/28 15:54:55 $ +% $Revision: 6641 $ $Date: 2005/10/28 15:54:55 $ % 'uh' uicontrol button height, in pixels; default = 22. % 'fus' frame/uicontrol spacing, in pixels; default = 8. @@ -52,7 +52,6 @@ % % d = dir; [s,v] = listdlg('PromptString','Select a file:','ListString',{d.name}); % -cfg_message(nargchk(1,inf,nargin,'struct')) figname = ''; smode = 2; % (multiple) diff --git a/spm.m b/spm.m index 1cb7e265..9bcebe61 100644 --- a/spm.m +++ b/spm.m @@ -53,7 +53,7 @@ % Copyright (C) 1991,1994-2015 Wellcome Trust Centre for Neuroimaging % Andrew Holmes -% $Id: spm.m 6409 2015-04-16 16:19:38Z guillaume $ +% $Id: spm.m 6524 2015-08-18 10:09:01Z guillaume $ %======================================================================= @@ -1197,6 +1197,7 @@ case lower(Modalities) %-Initialise SPM in PET, fMRI, EEG modality S = fscanf(fid,'%c'); fclose(fid); try + assignin('base','mfilename',@(varargin) mscript{i}); evalin('base',S); catch fprintf('Execution failed: %s\n',mscript{i}); diff --git a/spm_ADEM_update.m b/spm_ADEM_update.m index 31288258..aff357d6 100644 --- a/spm_ADEM_update.m +++ b/spm_ADEM_update.m @@ -15,7 +15,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_ADEM_update.m 6290 2014-12-20 22:11:50Z karl $ +% $Id: spm_ADEM_update.m 6506 2015-07-24 10:26:51Z karl $ % preliminaries @@ -30,7 +30,10 @@ % states %---------------------------------------------------------------------- - DEM.M(i).x = spm_unvec(DEM.qU.x{i}(:,end),DEM.M(i).x); + qE = DEM.qU.x{i}; + if ~isempty(qE) + DEM.M(i).x = spm_unvec(qE(:,end),DEM.M(i).x); + end % parameters %---------------------------------------------------------------------- @@ -68,4 +71,6 @@ DEM.G(i).v = spm_unvec(DEM.pU.v{i}(:,end),DEM.G(i).v); end end -DEM.G(n).a = spm_unvec(DEM.qU.a{n}(:,end),DEM.G(n).a); +if isfield(DEM.G,'a') + DEM.G(n).a = spm_unvec(DEM.qU.a{n}(:,end),DEM.G(n).a); +end diff --git a/spm_DEM.m b/spm_DEM.m index c9b5dd82..d623219b 100644 --- a/spm_DEM.m +++ b/spm_DEM.m @@ -90,7 +90,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_DEM.m 6290 2014-12-20 22:11:50Z karl $ +% $Id: spm_DEM.m 6502 2015-07-22 11:37:13Z karl $ % check model, data, priors and confounds and unpack @@ -328,7 +328,7 @@ Fi = -Inf; for iE = 1:nE - % get time and celar persistent variables in evaluation routines + % get time and clear persistent variables in evaluation routines %---------------------------------------------------------------------- tic; clear spm_DEM_eval diff --git a/spm_DEM_eval_diff.m b/spm_DEM_eval_diff.m index 67a1e41a..1ab94c68 100644 --- a/spm_DEM_eval_diff.m +++ b/spm_DEM_eval_diff.m @@ -17,7 +17,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_DEM_eval_diff.m 5691 2013-10-11 16:53:00Z karl $ +% $Id: spm_DEM_eval_diff.m 6502 2015-07-22 11:37:13Z karl $ % check for evaluation of bilinear terms %-------------------------------------------------------------------------- @@ -76,8 +76,7 @@ % inline function for evaluating projected parameters %-------------------------------------------------------------------------- -h = 'feval(f,x,v,spm_unvec(spm_vec(p) + u*q,p))'; -h = inline(h,'f','x','v','q','u','p'); +h = @(f,x,v,q,u,p) f(x,v,spm_unvec(spm_vec(p) + u*q,p)); for i = 1:(nl - 1) % states level i @@ -89,15 +88,15 @@ %---------------------------------------------------------------------- if bilinear && np try - [dgdxp dgdx] = spm_diff(h,M(i).gx,xvp{:},4,'q'); - [dgdvp dgdv] = spm_diff(h,M(i).gv,xvp{:},4,'q'); - [dfdxp dfdx] = spm_diff(h,M(i).fx,xvp{:},4,'q'); - [dfdvp dfdv] = spm_diff(h,M(i).fv,xvp{:},4,'q'); + [dgdxp, dgdx] = spm_diff(h,M(i).gx,xvp{:},4,'q'); + [dgdvp, dgdv] = spm_diff(h,M(i).gv,xvp{:},4,'q'); + [dfdxp, dfdx] = spm_diff(h,M(i).fx,xvp{:},4,'q'); + [dfdvp, dfdv] = spm_diff(h,M(i).fv,xvp{:},4,'q'); catch - [dgdxp dgdx] = spm_diff(h,M(i).g,xvp{:},[2 4]); - [dgdvp dgdv] = spm_diff(h,M(i).g,xvp{:},[3 4]); - [dfdxp dfdx] = spm_diff(h,M(i).f,xvp{:},[2 4]); - [dfdvp dfdv] = spm_diff(h,M(i).f,xvp{:},[3 4]); + [dgdxp, dgdx] = spm_diff(h,M(i).g,xvp{:},[2 4]); + [dgdvp, dgdv] = spm_diff(h,M(i).g,xvp{:},[3 4]); + [dfdxp, dfdx] = spm_diff(h,M(i).f,xvp{:},[2 4]); + [dfdvp, dfdv] = spm_diff(h,M(i).f,xvp{:},[3 4]); end else try diff --git a/spm_DEM_qP.m b/spm_DEM_qP.m index b0aabd48..f4ea1995 100644 --- a/spm_DEM_qP.m +++ b/spm_DEM_qP.m @@ -10,7 +10,7 @@ function spm_DEM_qP(qP,pP) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_DEM_qP.m 3878 2010-05-07 19:53:54Z karl $ +% $Id: spm_DEM_qP.m 6508 2015-07-25 15:23:25Z karl $ % unpack conditional covariances @@ -77,9 +77,7 @@ function spm_DEM_qP(qP,pP) % prior or true means %------------------------------------------------------------------ try - for k = 1:np - line([-1 1]/2 + k,[0 0] + pi(k),'LineWidth',4,'Color','k'); - end + hold on, bar(1:length(qi),pi,1/3), hold off end % labels @@ -87,7 +85,7 @@ function spm_DEM_qP(qP,pP) for k = 1:length(label) text(k + 1/4,qi(k),label{k},'FontSize',12,'FontWeight','Bold','Color','g'); end - Label = {Label{:}, label{:}}; + Label = [Label, label]; end end diff --git a/spm_DFP.m b/spm_DFP.m index f81cd864..cbd7fa06 100644 --- a/spm_DFP.m +++ b/spm_DFP.m @@ -77,12 +77,13 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_DFP.m 5219 2013-01-29 17:07:07Z spm $ +% $Id: spm_DFP.m 6540 2015-09-05 10:06:42Z karl $ % Check model, data, priros and confounds and unpack %-------------------------------------------------------------------------- [M,Y,U,X] = spm_DEM_set(DEM); +MOVIE = 0; % find or create a DEM figure %-------------------------------------------------------------------------- @@ -412,7 +413,7 @@ quy.v = qv; quy.y = qy; quy.u = qc; - [E dE] = spm_DEM_eval(M,quy,qp); + [E,dE] = spm_DEM_eval(M,quy,qp); dE.dP = [dE.dp spm_cat(dEdb)]; qu_c = qu_c*c; @@ -439,7 +440,7 @@ quy.v = qu(iP).v; quy.y = qy; quy.u = qc; - [e de] = spm_DEM_eval(M,quy,qp); + [e,de] = spm_DEM_eval(M,quy,qp); % conditional uncertainty about parameters %====================================================== @@ -502,9 +503,15 @@ % D-Step: save ensemble density and plot (over samples) %-------------------------------------------------------------- - QU{iY} = qu; + QU{iY} = qu; figure(Fdfp) spm_DFP_plot(QU,nY) + if MOVIE + subplot(2,1,1) + set(gca,'YLim',[-0.4 1.2]) + drawnow + MOV(iY) = getframe(gca); + end % Gradients and curvatures for E-Step: %============================================================== @@ -710,3 +717,11 @@ DEM.qH = qH; % conditional moments of hyper-parameters DEM.F = F; % [-ve] Free energy + +% set ButtonDownFcn +%-------------------------------------------------------------------------- +if MOVIE + figure(Fdfp), subplot(2,1,1) + set(gca,'Userdata',{MOV,16}) + set(gca,'ButtonDownFcn','spm_DEM_ButtonDownFcn') +end diff --git a/spm_DFP_plot.m b/spm_DFP_plot.m index b0e012c5..aea95ba4 100644 --- a/spm_DFP_plot.m +++ b/spm_DFP_plot.m @@ -9,7 +9,7 @@ function spm_DFP_plot(QU,pU) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_DFP_plot.m 1703 2008-05-21 13:59:23Z karl $ +% $Id: spm_DFP_plot.m 6540 2015-09-05 10:06:42Z karl $ % defaults for plotting %-------------------------------------------------------------------------- @@ -50,14 +50,18 @@ function spm_DFP_plot(QU,pU) for i = 1:nv plot(1:nt,V{i},':','Color',[1 1 1]/(2 + i - 1)) hold on - plot(nt,V{i}(nt,:),'.','Color',[1 1 1]/(2 + i - 1),'MarkerSize',32) - plot(nt,V{i}(nt,:),'.','Color',[1 1 1],'MarkerSize',4) + if np < 16 + plot(nt,V{i}(nt,:),'.','Color',[1 1 1]/(2 + i - 1),'MarkerSize',32) + plot(nt,V{i}(nt,:),'.','Color',[1 1 1],'MarkerSize',4) + else + plot(nt,V{i}(nt,:),'.','Color',[1 0 0],'MarkerSize',8) + end plot(1:nt,mean(V{i},2),'--b','LineWidth',2) hold on end try hold on - plot([1:nt] - 1,pV,'r') + plot((1:nt) - 1,pV,'r') end hold off title('causes','FontSize',16); @@ -75,15 +79,18 @@ function spm_DFP_plot(QU,pU) for i = 1:nx plot(1:nt,X{i},':','Color',[1 1 1]/(2 + i - 1)) hold on - plot(nt,X{i}(nt,:),'.','Color',[1 1 1]/(2 + i - 1),'MarkerSize',32) - plot(nt,X{i}(nt,:),'.','Color',[1 1 1],'MarkerSize',4) - + if np < 16 + plot(nt,X{i}(nt,:),'.','Color',[1 1 1]/(2 + i - 1),'MarkerSize',32) + plot(nt,X{i}(nt,:),'.','Color',[1 1 1],'MarkerSize',4) + else + plot(nt,X{i}(nt,:),'.','Color',[1 0 0],'MarkerSize',8) + end plot(1:nt,mean(X{i},2),'--b','LineWidth',2) hold on end try hold on - plot([1:nt] - 1,pX,'r') + plot((1:nt) - 1,pX,'r') end hold off title('hidden states','FontSize',16); diff --git a/spm_LAP.m b/spm_LAP.m index 441df81a..566c0ffa 100644 --- a/spm_LAP.m +++ b/spm_LAP.m @@ -89,7 +89,7 @@ % Copyright (C) 2010-2013 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_LAP.m 6018 2014-05-25 09:24:14Z karl $ +% $Id: spm_LAP.m 6508 2015-07-25 15:23:25Z karl $ % find or create a DEM figure @@ -121,8 +121,8 @@ % number of iterations %-------------------------------------------------------------------------- -try, nD = M(1).E.nD; catch, nD = 1; end -try, nN = M(1).E.nN; catch, nN = 16; end +try, nD = M(1).E.nD; catch, nD = 1; end +try, nN = M(1).E.nN; catch, nN = 8; end % ensure integration scheme evaluates gradients at each time-step diff --git a/spm_Menu.fig b/spm_Menu.fig index 1a977743..38fdf8ff 100644 Binary files a/spm_Menu.fig and b/spm_Menu.fig differ diff --git a/spm_add.mexa64 b/spm_add.mexa64 index 01ffbea4..2b9e199b 100755 Binary files a/spm_add.mexa64 and b/spm_add.mexa64 differ diff --git a/spm_add.mexmaci64 b/spm_add.mexmaci64 index e7fb8b9d..8b8549d5 100755 Binary files a/spm_add.mexmaci64 and b/spm_add.mexmaci64 differ diff --git a/spm_add.mexw32 b/spm_add.mexw32 index aacdf62f..00abc5ee 100755 Binary files a/spm_add.mexw32 and b/spm_add.mexw32 differ diff --git a/spm_add.mexw64 b/spm_add.mexw64 index 87003e96..eef09dbf 100755 Binary files a/spm_add.mexw64 and b/spm_add.mexw64 differ diff --git a/spm_atlas.m b/spm_atlas.m index 2e0ed773..bd306e0f 100644 --- a/spm_atlas.m +++ b/spm_atlas.m @@ -5,7 +5,7 @@ % FORMAT [S,sts] = spm_atlas('select',xA,label) % FORMAT Q = spm_atlas('query',xA,XYZmm) % FORMAT [Q,P] = spm_atlas('query',xA,xY) -% FORMAT VM = spm_atlas('mask',xA,label) +% FORMAT VM = spm_atlas('mask',xA,label,opt) % FORMAT V = spm_atlas('prob',xA,label) % FORMAT V = spm_atlas('maxprob',xA,thresh) % FORMAT D = spm_atlas('dir') @@ -17,7 +17,7 @@ % Copyright (C) 2013-2015 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_atlas.m 6448 2015-05-22 18:31:04Z guillaume $ +% $Id: spm_atlas.m 6656 2015-12-24 16:49:52Z guillaume $ if ~nargin, action = 'load'; end @@ -245,8 +245,8 @@ %========================================================================== case 'mask' %========================================================================== - % FORMAT VM = spm_atlas('mask',xA,label) - %-Return binary mask for given labels + % FORMAT VM = spm_atlas('mask',xA,label,opt) + %-Return (binary) mask for given labels if nargin < 2, xA = ''; else xA = varargin{1}; end xA = spm_atlas('load',xA); @@ -255,24 +255,39 @@ label = filter_labels(xA,label); if numel(xA.VA) == 1 % or xA.info.type contains type definition + if nargin < 4 || isempty(varargin{3}), opt = 'binary'; + else opt = lower(varargin{3}); end VM = struct(... 'fname', [xA.info.name '_mask' spm_file_ext],... 'dim', xA.VA(1).dim,... - 'dt', [spm_type('uint8') spm_platform('bigend')],... + 'dt', [spm_type('uint16') spm_platform('bigend')],... 'mat', xA.VA(1).mat,... 'n', 1,... 'pinfo', [1 0 0]',... 'descrip', sprintf('%s mask',xA.info.name)); - VM.dat = false(VM.dim); + if strcmp(opt,'binary') + VM.dat = false(VM.dim); + else + VM.dat = uint16(zeros(VM.dim)); + end D = spm_read_vols(xA.VA); for i=1:numel(label) j = find(ismember({xA.labels.name},label{i})); for k=1:numel(j) - VM.dat = VM.dat | (D == xA.labels(j(k)).index); + idx = xA.labels(j(k)).index; + if strcmp(opt,'binary') + VM.dat = VM.dat | (D == idx); + elseif strcmp(opt,'atlas') + VM.dat(D == idx) = i; + elseif strcmp(opt,'preserve') + VM.dat(D == idx) = idx; + else + error('Unknown option.'); + end end end - VM.dat = uint8(VM.dat); + VM.dat = uint16(VM.dat); else if nargin < 4, thresh = 0.5; else thresh = varargin{3}; end VM = spm_atlas('prob',xA,label); diff --git a/spm_bms_anova_img.m b/spm_bms_anova_img.m index 1a2381fa..d57f14b5 100644 --- a/spm_bms_anova_img.m +++ b/spm_bms_anova_img.m @@ -7,14 +7,14 @@ % prior Specification of a single group is equivalent to a one sample t-test. % For this case you can specify 'unit' or 'jzs' (default) priors % See spm_bms_ttest.m and spm_bms_anova.m for more details -%_______________________________________________________________________ -% Copyright (C) 2014 Wellcome Trust Centre for Neuroimaging +%__________________________________________________________________________ +% Copyright (C) 2014-2015 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_bms_anova_img.m 6038 2014-06-04 15:22:42Z will $ +% $Id: spm_bms_anova_img.m 6654 2015-12-22 12:55:36Z spm $ % Select files and groups if not provided -if nargin < 2 | isempty(P) | isempty(g) +if nargin < 2 || isempty(P) || isempty(g) Ng=input('Enter number of groups '); P=[];g=[]; for j=1:Ng, @@ -28,19 +28,17 @@ Ng=length(unique(g)); end -try - prior=prior; -catch - prior='jzs'; +if nargin < 3 + prior = 'jzs'; end -VY = spm_data_hdr_read(P); -M = VY{1}.mat; -DIM = VY{1}.dim(1:3); +VY = spm_data_hdr_read(P); +M = VY{1}.mat; +DIM = VY{1}.dim(1:3); YNaNrep = spm_type(VY{1}.dt(1),'nanrep'); -mask = true(DIM); +mask = true(DIM); -fn=['logBF_alt_',prior]; +fn = ['logBF_alt_',prior]; V = struct(... 'fname', [fn spm_file_ext],... 'dim', DIM,... @@ -50,7 +48,7 @@ 'descrip', 'spm_bms_anova:LogBF against null'); V = spm_data_hdr_write(V); -nScan=length(VY); +nScan = length(VY); chunksize = floor(spm_get_defaults('stats.maxmem') / 8 / nScan); nbchunks = ceil(prod(DIM) / chunksize); chunks = min(cumsum([1 repmat(chunksize,1,nbchunks)]),prod(DIM)+1); @@ -84,14 +82,14 @@ Y = Y(:,cmask); %-Data within mask Nvoxels=size(Y,2); - for n=1:Nvoxels, + for n=1:Nvoxels if Ng==1 logBF(n)=spm_bms_ttest(Y(:,n),prior); else logBF(n)=spm_bms_anova(Y(:,n),g); end if rem(n,1000)==0 - disp(sprintf('Voxel %d out of %d',n,Nvoxels)); + fprintf('Voxel %d out of %d\n',n,Nvoxels); %-# end end @@ -109,5 +107,3 @@ fprintf('\n'); %-# spm_progress_bar('Clear'); - -end diff --git a/spm_bsplinc.mexa64 b/spm_bsplinc.mexa64 index f8b06f57..dd90c1b7 100755 Binary files a/spm_bsplinc.mexa64 and b/spm_bsplinc.mexa64 differ diff --git a/spm_bsplinc.mexmaci64 b/spm_bsplinc.mexmaci64 index f086f702..f8498678 100755 Binary files a/spm_bsplinc.mexmaci64 and b/spm_bsplinc.mexmaci64 differ diff --git a/spm_bsplinc.mexw32 b/spm_bsplinc.mexw32 index f9927abb..aade8ae3 100755 Binary files a/spm_bsplinc.mexw32 and b/spm_bsplinc.mexw32 differ diff --git a/spm_bsplinc.mexw64 b/spm_bsplinc.mexw64 index 826bdbf2..4d1e889e 100755 Binary files a/spm_bsplinc.mexw64 and b/spm_bsplinc.mexw64 differ diff --git a/spm_bsplins.mexw32 b/spm_bsplins.mexw32 index 107d23e7..dec0528b 100755 Binary files a/spm_bsplins.mexw32 and b/spm_bsplins.mexw32 differ diff --git a/spm_bsplins.mexw64 b/spm_bsplins.mexw64 index 9ef277f0..033eb5a8 100755 Binary files a/spm_bsplins.mexw64 and b/spm_bsplins.mexw64 differ diff --git a/spm_bwlabel.mexw32 b/spm_bwlabel.mexw32 index e1eb2e5f..08f8286c 100755 Binary files a/spm_bwlabel.mexw32 and b/spm_bwlabel.mexw32 differ diff --git a/spm_bwlabel.mexw64 b/spm_bwlabel.mexw64 index a3823b11..c8cda6dd 100755 Binary files a/spm_bwlabel.mexw64 and b/spm_bwlabel.mexw64 differ diff --git a/spm_cat.mexw32 b/spm_cat.mexw32 index 20b495fa..4d26dfb7 100644 Binary files a/spm_cat.mexw32 and b/spm_cat.mexw32 differ diff --git a/spm_cat.mexw64 b/spm_cat.mexw64 index 2c498fe3..281c0dc0 100644 Binary files a/spm_cat.mexw64 and b/spm_cat.mexw64 differ diff --git a/spm_check_installation.m b/spm_check_installation.m index 027a0169..0886d244 100644 --- a/spm_check_installation.m +++ b/spm_check_installation.m @@ -13,10 +13,10 @@ % Build signature of SPM distribution as used by 'full' option. % (for developers) %__________________________________________________________________________ -% Copyright (C) 2009-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2009-2016 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_check_installation.m 6444 2015-05-21 11:15:48Z guillaume $ +% $Id: spm_check_installation.m 6675 2016-01-12 18:22:29Z guillaume $ if isdeployed, return; end @@ -207,7 +207,7 @@ %-Detect SPM toolboxes %-------------------------------------------------------------------------- officials = {'DARTEL', 'dcm_fnirs', 'dcm_meeg', 'DEM', 'FieldMap', ... - 'Longitudinal', 'MEEGtools', 'mixture', 'mlm', 'Neural_Models', ... + 'Longitudinal', 'mci', 'MEEGtools', 'mixture', 'mlm', 'Neural_Models', ... 'OldNorm', 'OldSeg', 'Shoot', 'spectral', 'SPEM_and_DCM', 'SRender'}; dd = dir(fullfile(SPMdir,'toolbox')); dd = {dd([dd.isdir]).name}; diff --git a/spm_contrasts.m b/spm_contrasts.m index 767a09be..16514f58 100644 --- a/spm_contrasts.m +++ b/spm_contrasts.m @@ -8,10 +8,10 @@ % This function fills in SPM.xCon and writes con_????, ess_???? and % spm?_???? images. %__________________________________________________________________________ -% Copyright (C) 2002-2012 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2002-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston, Will Penny & Guillaume Flandin -% $Id: spm_contrasts.m 6289 2014-12-18 15:55:02Z guillaume $ +% $Id: spm_contrasts.m 6490 2015-06-26 11:51:46Z guillaume $ % Temporary copy of the SPM variable, to avoid saving it in SPM.mat unless @@ -53,8 +53,14 @@ VHp = SPM.VResMS; end -if spm_mesh_detect(Vbeta), file_ext = '.gii'; -else file_ext = spm_file_ext; end +if spm_mesh_detect(Vbeta) + file_ext = '.gii'; + g = SPM.xY.VY(1).private; + metadata = {g.private.metadata(1).name, g.private.metadata(1).value}; +else + file_ext = spm_file_ext; + metadata = {}; +end %-Compute & store contrast parameters, contrast/ESS images, & SPM images %========================================================================== @@ -111,7 +117,8 @@ 'dt', [spm_type('float32'), spm_platform('bigend')],... 'mat', SPM.xVol.M,... 'pinfo', [1,0,0]',... - 'descrip',sprintf('Contrast %d: %s',ic,xCon(ic).name)); + 'descrip',sprintf('Contrast %d: %s',ic,xCon(ic).name),... + metadata{:}); xCon(ic).Vcon = spm_data_hdr_write(xCon(ic).Vcon); @@ -150,7 +157,8 @@ 'dt', [spm_type('float32'), spm_platform('bigend')],... 'mat', SPM.xVol.M,... 'pinfo', [1,0,0]',... - 'descrip',sprintf('ESS contrast %d: %s',ic,xCon(ic).name)); + 'descrip',sprintf('ESS contrast %d: %s',ic,xCon(ic).name),... + metadata{:}); xCon(ic).Vcon = spm_data_hdr_write(xCon(ic).Vcon); @@ -210,7 +218,7 @@ % Simple contrast - Gaussian distributed c = xCon(ic).c; - cB = spm_get_data(xCon(ic).Vcon,XYZ); + cB = spm_data_read(xCon(ic).Vcon,'xyz',XYZ); if isfield(SPM.PPM,'VB'); % If posterior sd image for that contrast does % not already exist, then compute it @@ -220,7 +228,7 @@ SPM = spm_vb_contrasts(SPM,XYZ,xCon,ic); end % Read in posterior sd image for contrast - Vsd = spm_get_data(SPM.PPM.Vcon_sd(ic),XYZ); + Vsd = spm_data_read(SPM.PPM.Vcon_sd(ic),'xyz',XYZ); VcB = Vsd.^2; else VcB = c'*SPM.PPM.Cby*c; @@ -228,7 +236,7 @@ % hyperparameter and Taylor approximation %---------------------------------------------- - l = spm_get_data(SPM.VHp(j),XYZ); + l = spm_data_read(SPM.VHp(j),'xyz',XYZ); VcB = VcB + (c'*SPM.PPM.dC{j}*c)*(l - SPM.PPM.l(j)); end end @@ -246,7 +254,7 @@ % Compound contrast - Log Bayes Factor fprintf('\t\t%-75s\n','Log Bayes Factor for compound contrast'); fprintf('\t%-32s: %29s\n',' ',' '); - Z = spm_get_data(xCon(ic).Vcon,XYZ); + Z = spm_data_read(xCon(ic).Vcon,'xyz',XYZ); str = sprintf('[%1.2f]',xCon(ic).eidf); end @@ -275,7 +283,8 @@ 'mat', SPM.xVol.M,... 'pinfo', [1,0,0]',... 'descrip',sprintf('SPM{%s_%s} - contrast %d: %s',... - xCon(ic).STAT,str,ic,xCon(ic).name)); + xCon(ic).STAT,str,ic,xCon(ic).name),... + metadata{:}); xCon(ic).Vspm = spm_data_hdr_write(xCon(ic).Vspm); diff --git a/spm_conv_vol.mexa64 b/spm_conv_vol.mexa64 index f996dc89..5e36bcfd 100755 Binary files a/spm_conv_vol.mexa64 and b/spm_conv_vol.mexa64 differ diff --git a/spm_conv_vol.mexmaci64 b/spm_conv_vol.mexmaci64 index cd362a79..6e7cbecd 100755 Binary files a/spm_conv_vol.mexmaci64 and b/spm_conv_vol.mexmaci64 differ diff --git a/spm_conv_vol.mexw32 b/spm_conv_vol.mexw32 index 616656ae..42ce0ad4 100755 Binary files a/spm_conv_vol.mexw32 and b/spm_conv_vol.mexw32 differ diff --git a/spm_conv_vol.mexw64 b/spm_conv_vol.mexw64 index 4cef5cdd..27873201 100755 Binary files a/spm_conv_vol.mexw64 and b/spm_conv_vol.mexw64 differ diff --git a/spm_cross.m b/spm_cross.m new file mode 100644 index 00000000..df568ba8 --- /dev/null +++ b/spm_cross.m @@ -0,0 +1,30 @@ +function [Y] = spm_cross(X,x) +% Multidimensional cross (outer) product +% FORMAT [Y] = spm_cross(X,x) +% +% X - numeric array +% x - vector +% +% Y - outer product +% +% See also: spm_dot +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_cross.m 6654 2015-12-22 12:55:36Z spm $ + + +% inner product +%-------------------------------------------------------------------------- +if isvector(X), Y = X(:)*x(:)'; return, end + +d = size(X); +ind = repmat(':,',1,numel(d)); +ind = ind(1:end - 1); + +d(end + 1) = 1; +Y = zeros(d); +for i = 1:numel(x) + eval(['Y(' ind ',' num2str(i) ') = X*x(i);']); +end diff --git a/spm_csd_fmri_mtf.m b/spm_csd_fmri_mtf.m index f453dd8c..c7b9e7aa 100644 --- a/spm_csd_fmri_mtf.m +++ b/spm_csd_fmri_mtf.m @@ -26,7 +26,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_csd_fmri_mtf.m 6075 2014-06-29 21:11:40Z karl $ +% $Id: spm_csd_fmri_mtf.m 6560 2015-09-23 13:50:43Z karl $ % compute log-spectral density @@ -63,44 +63,43 @@ % amplitude scaling constant %-------------------------------------------------------------------------- -C = 1/256; +C = 1/512; % neuronal fluctuations (Gu) (1/f or AR(1) form) %-------------------------------------------------------------------------- for i = 1:nu if strcmp(form,'1/f') - G = exp(P.a(1,i))*w.^(-exp(P.a(2,i)))*4; + G = exp(P.a(1,i))*w.^(-exp(P.a(2,i))); else - G = exp(P.a(1,i))*spm_mar2csd(exp(P.a(2,i))/2,w,M.ns); + G = exp(P.a(1,i))*spm_mar2csd(exp(P.a(2,i))/2,w); end Gu(:,i,i) = Gu(:,i,i) + C*G; end -% observation noise (1/f or AR(1) form) +% region specific observation noise (1/f or AR(1) form) %-------------------------------------------------------------------------- for i = 1:nn - - % global component - %---------------------------------------------------------------------- - for j = 1:nn - - if strcmp(form,'1/f') - G = exp(P.b(1,1))*w.^(-exp(P.b(2,1))/2)/8; - else - G = exp(P.b(1,1))*spm_mar2csd(exp(P.b(2,1))/2,w,M.ns)/64; - end - Gn(:,i,j) = Gn(:,i,j) + C*G; - end - - % region specific - %---------------------------------------------------------------------- if strcmp(form,'1/f') G = exp(P.c(1,i))*w.^(-exp(P.c(2,i))/2); else - G = exp(P.c(1,i))*spm_mar2csd(exp(P.c(2,i))/2,w,M.ns)/8; + G = exp(P.c(1,i))*spm_mar2csd(exp(P.c(2,i))/2,w); end Gn(:,i,i) = Gn(:,i,i) + C*G; - +end + + +% global components +%-------------------------------------------------------------------------- +if strcmp(form,'1/f') + G = exp(P.b(1,1))*w.^(-exp(P.b(2,1))/2); +else + G = exp(P.b(1,1))*spm_mar2csd(exp(P.b(2,1))/2,w); +end +for i = 1:nn + for j = i:nn + Gn(:,i,j) = Gn(:,i,j) + C*G; + Gn(:,j,i) = Gn(:,i,j); + end end diff --git a/spm_data_hdr_write.m b/spm_data_hdr_write.m index 4c488702..8ad55990 100644 --- a/spm_data_hdr_write.m +++ b/spm_data_hdr_write.m @@ -3,10 +3,10 @@ % FORMAT V = spm_data_hdr_write(V) % V - a structure array (see spm_data_hdr_read) %__________________________________________________________________________ -% Copyright (C) 2012-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2012-20154 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_data_hdr_write.m 5916 2014-03-13 13:15:02Z guillaume $ +% $Id: spm_data_hdr_write.m 6486 2015-06-24 16:27:17Z guillaume $ switch lower(spm_file(V(1).fname,'ext')) @@ -18,12 +18,17 @@ V(i).private = gifti(struct('cdata',zeros(V(i).dim))); V(i).private.private.data{1}.attributes.DataType = ... ['NIFTI_TYPE_' upper(spm_type(V(i).dt(1)))]; + if isfield(V(i),'SurfaceID') + V(i).private.private.metadata(1).name = 'SurfaceID'; + V(i).private.private.metadata(1).value = V(i).SurfaceID; + end % see also endianness, scale/offset/offset_byte, metadata save(V(i).private, V(i).fname, 'ExternalFileBinary'); V(i).private = gifti(V(i).fname); %V(i).private.dat = file_array(spm_file(V(i).fname,'ext','dat'), ... % V(i).dim, spm_type(V(i).dt(1))); end + if isfield(V,'SurfaceID'), V = rmfield(V,'SurfaceID'); end otherwise error('File "%s" is not of a recognised type.', V(1).fname); diff --git a/spm_data_read.m b/spm_data_read.m index 28febf5c..1c46420e 100644 --- a/spm_data_read.m +++ b/spm_data_read.m @@ -22,7 +22,7 @@ % Copyright (C) 2012 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_data_read.m 5916 2014-03-13 13:15:02Z guillaume $ +% $Id: spm_data_read.m 6486 2015-06-24 16:27:17Z guillaume $ if ~isstruct(V) @@ -30,7 +30,7 @@ end cl = class(V(1).private); -if isfield(V(1),'dat'), cl = 'nifti'; end +if isfield(V(1),'dat') && ~isequal(cl,'gifti'), cl = 'nifti'; end switch cl case 'nifti' diff --git a/spm_dcm_U.m b/spm_dcm_U.m index 6bec0db1..c1ee2597 100644 --- a/spm_dcm_U.m +++ b/spm_dcm_U.m @@ -23,7 +23,7 @@ % Copyright (C) 2003-2014 Wellcome Trust Centre for Neuroimaging % Will Penny & Klaas Enno Stephan -% $Id: spm_dcm_U.m 6006 2014-05-21 18:09:05Z guillaume $ +% $Id: spm_dcm_U.m 6613 2015-11-29 10:56:53Z peter $ %-Load DCM and SPM files @@ -59,12 +59,17 @@ %-------------------------------------------------------------------------- U.name = {}; U.u = []; -U.dt = DCM.U.dt; +try + U.dt = DCM.U.dt; +catch + U.dt = Sess.U(1).dt; +end for i = 1:numel(inputs) if any(inputs{i}) mo = find(inputs{i}); - if (length(mo)-1) > length(Sess.U(i).P) - error(['More parametric modulations specified than exist ' ... + num_regressors = size(Sess.U(i).u,2); + if length(mo) > num_regressors + error(['More regressors specified than exist ' ... 'for input %s in SPM.mat.'],Sess.U(i).name{1}); end for j=mo diff --git a/spm_dcm_average.m b/spm_dcm_average.m index 0f7a9587..52e70f23 100644 --- a/spm_dcm_average.m +++ b/spm_dcm_average.m @@ -32,7 +32,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Will Penny & Klaas Enno Stephan -% $Id: spm_dcm_average.m 5900 2014-02-27 21:54:51Z karl $ +% $Id: spm_dcm_average.m 6509 2015-07-30 11:54:48Z peter $ % Preiminaries @@ -107,7 +107,7 @@ if graphics T(model) = trace(Cp); H(model) = spm_logdet(miCp(:,:,model)); - F(model) = DCM.F; + Fs(model) = DCM.F; end end @@ -118,7 +118,7 @@ if graphics spm_figure('GetWin','BPA'); - subplot(3,1,1), bar(F) + subplot(3,1,1), bar(Fs) title('Free energy','FontSize',16) xlabel('Subject'), axis square diff --git a/spm_dcm_bmc_peb.m b/spm_dcm_bmc_peb.m index 590797f0..0b3b9664 100644 --- a/spm_dcm_bmc_peb.m +++ b/spm_dcm_bmc_peb.m @@ -1,5 +1,5 @@ function [BMC,PEB] = spm_dcm_bmc_peb(DCM,M,field) -% hierarchical (PEB) model comparison and averaging (1st level) +% hierarchical (PEB) model comparison and averaging (1st and 2nd level) % FORMAT [BMC,PEB] = spm_dcm_bmc_peb(DCM,[M,field]) % % DCM - {N [x M]} structure array of DCMs from N subjects @@ -70,7 +70,7 @@ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_bmc_peb.m 6427 2015-05-05 15:42:35Z karl $ +% $Id: spm_dcm_bmc_peb.m 6473 2015-06-04 19:05:05Z karl $ % set up @@ -113,33 +113,32 @@ % invert under full second level model %---------------------------------------------------------------------- - PEB = spm_dcm_peb(DCM(:,i),M,field); + PEB = spm_dcm_peb(DCM(:,i),M,field); % Get priors and posteriors - of first and second order parameters %---------------------------------------------------------------------- Np = size(PEB.Ep,1); - q = 1:(Nx*Np); + qE = spm_vec(PEB.Ep); + qC = PEB.Cp; + pE = spm_vec(PEB.M.pE); + pC = PEB.M.pC; - qE = spm_vec(PEB.Ep,PEB.Eh); - qC = PEB.Cph; - pE = spm_vec(PEB.M.pE,PEB.M.hE); - pC = blkdiag(PEB.M.pC,PEB.M.hC); - for k = 1:Nk % second level model reduction %------------------------------------------------------------------ - R = kron(diag(K(k,:)),speye(Np,Np)); - rE = spm_vec(R*PEB.M.pE, PEB.M.hE); - rC = blkdiag(R*PEB.M.pC*R,PEB.M.hC); + R = diag(K(k,:)); + R = kron(R,speye(Np,Np)); + rE = R*pE; + rC = R*pC*R; % Bayesian model reduction (of second level) %------------------------------------------------------------------ - [sF, sE, sC] = spm_log_evidence_reduce(qE,qC,pE,pC,rE,rC); + [sF,sE,sC] = spm_log_evidence_reduce(qE,qC,pE,pC,rE,rC); peb{i,k} = PEB; - peb{i,k}.Ep(:) = sE(q); - peb{i,k}.Cp = sC(q,q); + peb{i,k}.Ep(:) = sE; + peb{i,k}.Cp = sC; peb{i,k}.F = PEB.F + sF; F(i,k) = peb{i,k}.F; @@ -172,9 +171,9 @@ BMC.Pw = Pw; BMC.K = K; - % Show results %========================================================================== +if isfield(M,'noplot'), return, end spm_figure('Getwin','PEB-BMC'); clf subplot(3,2,1), [m,i] = max(Pw); diff --git a/spm_dcm_bmr.m b/spm_dcm_bmr.m index e7cf8226..ffc0c3c5 100644 --- a/spm_dcm_bmr.m +++ b/spm_dcm_bmr.m @@ -1,6 +1,6 @@ -function [RCM,BMR,BMA] = spm_dcm_bmr(P,field) +function [RCM,BMC,BMA] = spm_dcm_bmr(P,field) % Bayesian model reduction (under Laplace approximation) -% FORMAT [RCM,BMR,BMA] = spm_dcm_bmr(P,[field]) +% FORMAT [RCM,BMC,BMA] = spm_dcm_bmr(P,[field]) % % P - {Nsub x Nmodel} cell array of DCM filenames or model structures % of Nsub subjects, where each model is reduced independently @@ -8,10 +8,10 @@ % field - parameter fields in DCM{i}.Ep to plot [default: {'A','B'}] % % RCM - reduced DCM array -% BMR - (Nsub) summary structure -% BMR.name - character/cell array of DCM filenames -% BMR.F - their associated free energies -% BMR.P - and posterior (model) probabilities +% BMC - (Nsub) summary structure +% BMC.name - character/cell array of DCM filenames +% BMC.F - their associated free energies +% BMC.P - and posterior (model) probabilities % BMA - Baysian model average (see spm_dcm_bma) %__________________________________________________________________________ % @@ -38,7 +38,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_bmr.m 6427 2015-05-05 15:42:35Z karl $ +% $Id: spm_dcm_bmr.m 6474 2015-06-06 10:41:55Z karl $ % get filenames and set up @@ -59,14 +59,14 @@ field = {field}; end -% number of subjects and models: BMR over models (rows) for each subject +% number of subjects and models: BMC over models (rows) for each subject %-------------------------------------------------------------------------- [Ns,N] = size(P); if Ns > 2 for i = 1:Ns [p,q] = spm_dcm_bmr(P(i,:),field); RCM(i,:) = p; - BMR(i) = q; + BMC(i) = q; end return end @@ -74,7 +74,7 @@ % exhaustive search %-------------------------------------------------------------------------- if N < 2 - [RCM,BMR,BMA] = spm_dcm_bmr_all(P{1},field); + [RCM,BMC,BMA] = spm_dcm_bmr_all(P{1},field); return end @@ -157,7 +157,7 @@ % Save DCM and record free-energy %---------------------------------------------------------------------- - name{j} = ['BMR_' DCMname]; + name{j} = ['BMC_' DCMname]; RCM{j} = DCM; G(j) = DCM.F; @@ -171,9 +171,9 @@ %-summary structure %-------------------------------------------------------------------------- -BMR.name = name; -BMR.F = G; -BMR.P = p; +BMC.name = name; +BMC.F = G; +BMC.P = p; % Get and display selected model %========================================================================== diff --git a/spm_dcm_bmr_all.m b/spm_dcm_bmr_all.m index 2538dd34..6ea405c2 100644 --- a/spm_dcm_bmr_all.m +++ b/spm_dcm_bmr_all.m @@ -2,19 +2,24 @@ % Bayesian model reduction of all permutations of model parameters % FORMAT [RCM,BMR,BMA] = spm_dcm_bmr_all(DCM,field) % -% DCM - DCM structures: -% DCM.M.pE - prior expectation (with parameters in pE.A, pE.B and pE.C) +% DCM - DCM structures: +% +% DCM.M.pE - prior expectation % DCM.M.pC - prior covariance -% DCM.Ep - posterior expectation: Bayesian model average -% DCM.Cp - posterior covariances; Bayesian model average -% DCM.Pp - Model posterior (with and without each parameter) +% DCM.Ep - posterior expectation +% DCM.Cp - posterior covariances % % field - parameter fields in DCM{i}.Ep to optimise [default: {'A','B'}] % 'All' will invoke all fields (i.e. random effects) % If Ep is not a structure, all parameters will be considered % -% % RCM - reduced DCM array +% RCM.M.pE - prior expectation (with parameters in pE.A, pE.B and pE.C) +% RCM.M.pC - prior covariance +% RCM.Ep - posterior expectation: Bayesian model average +% RCM.Cp - posterior covariances; Bayesian model average +% RCM.Pp - Model posterior (with and without each parameter) +% % BMR - (Nsub) summary structure % BMR.name - character/cell array of DCM filenames % BMR.F - their associated free energies @@ -35,13 +40,13 @@ % are retained in the best model or there are no more parameters to % consider. % -% See also: spm_dcm_post_hoc – this routine is essentially a simplified +% See also: spm_dcm_post_hoc - this routine is essentially a simplified % version of spm_dcm_post_hoc %__________________________________________________________________________ % Copyright (C) 2010-2014 Wellcome Trust Centre for Neuroimaging % Karl Friston, Peter Zeidman -% $Id: spm_dcm_bmr_all.m 6427 2015-05-05 15:42:35Z karl $ +% $Id: spm_dcm_bmr_all.m 6656 2015-12-24 16:49:52Z guillaume $ %-Number of parameters to consider before invoking greedy search @@ -53,6 +58,9 @@ if nargin < 2 || isempty(field) field = {'A','B'}; end +if ischar(field) + field = {field}; +end %-dela with filenames stucture %-------------------------------------------------------------------------- @@ -191,7 +199,7 @@ Pk(2,i) = mean(p( K(:,i))); end Pk = Pk(1,:)./sum(Pk); -Pp = full(C); +Pp = double(full(C)); Pp(k) = Pk; @@ -253,15 +261,20 @@ subplot(3,2,4), spm_plot_ci(Ep(i),abs(Cp(i))) title('MAP (reduced)','FontSize',16), axis square, axis(a) -subplot(3,2,5), imagesc(K') +subplot(3,2,5), imagesc(1 - K') xlabel('model'), ylabel('parameter'), title('model space','FontSize',16) set(gca,'YTickLabel',BMR.name); axis tight, axis square -subplot(3,2,6), bar(diag(Pp(i)),length(i)) +subplot(3,2,6) +Np = length(i); +if Np > 1 + bar(diag(Pp(i)),Np) +else + bar(Pp) +end xlabel('parameter'), title(' posterior','FontSize',16) -spm_axis tight, axis square -drawnow +axis square, drawnow %-Save Bayesian parameter average and family-wise model inference diff --git a/spm_dcm_bpa.m b/spm_dcm_bpa.m index f27cddfd..8e016a56 100644 --- a/spm_dcm_bpa.m +++ b/spm_dcm_bpa.m @@ -43,7 +43,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Will Penny & Klaas Enno Stephan -% $Id: spm_dcm_bpa.m 6309 2015-01-20 21:01:36Z spm $ +% $Id: spm_dcm_bpa.m 6620 2015-12-02 17:35:13Z peter $ % Preiminaries @@ -67,7 +67,7 @@ % loop over models in each column %---------------------------------------------------------------------- for i = 1:size(P,2) - BPA(i) = spm_dcm_bpa(P(:,i),nocd); + BPA{i} = spm_dcm_bpa(P(:,i),nocd); end return diff --git a/spm_dcm_estimate.m b/spm_dcm_estimate.m index 5130b876..447340b5 100644 --- a/spm_dcm_estimate.m +++ b/spm_dcm_estimate.m @@ -26,8 +26,10 @@ % DCM.options.induced % switch for CSD data features % DCM.options.P % starting estimates for parameters % DCM.options.hidden % indices of hidden regions -% DCM.options.nmax % maximum number of (effective) nodes -% DCM.options.nN % maximum number of iterations +% DCM.options.maxnodes % maximum number of (effective) nodes +% DCM.options.maxit % maximum number of iterations +% DCM.options.hE % expected precision of the noise +% DCM.options.hC % variance of noise expectation % % Evaluates: %-------------------------------------------------------------------------- @@ -53,10 +55,10 @@ % Copyright (C) 2002-2012 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_estimate.m 6432 2015-05-09 12:58:12Z karl $ +% $Id: spm_dcm_estimate.m 6591 2015-11-06 11:18:19Z peter $ -SVNid = '$Rev: 6432 $'; +SVNid = '$Rev: 6591 $'; %-Load DCM structure %-------------------------------------------------------------------------- @@ -89,15 +91,41 @@ try, DCM.options.stochastic; catch, DCM.options.stochastic = 0; end try, DCM.options.nonlinear; catch, DCM.options.nonlinear = 0; end try, DCM.options.centre; catch, DCM.options.centre = 0; end -try, DCM.options.nmax; catch, DCM.options.nmax = 8; end -try, DCM.options.nN; catch, DCM.options.nN = 32; end try, DCM.options.hidden; catch, DCM.options.hidden = []; end +try, DCM.options.hE; catch, DCM.options.hE = 6; end +try, DCM.options.hC; catch, DCM.options.hC = 1/128; end try, DCM.n; catch, DCM.n = size(DCM.a,1); end try, DCM.v; catch, DCM.v = size(DCM.Y.y,1); end try, M.nograph = DCM.options.nograph; catch, M.nograph = spm('CmdLine');end -try, M.Nmax = DCM.options.nN ; catch,M.Nmax = 64; end -try, M.Nmax = DCM.M.Nmax ; catch,M.Nmax = 64; end + +% check max iterations +try + DCM.options.maxit; +catch + if isfield(DCM.options,'nN') + DCM.options.maxit = DCM.options.nN; + warning('options.nN is deprecated. Please use options.maxit'); + elseif DCM.options.stochastic + DCM.options.maxit = 32; + else + DCM.options.maxit = 128; + end +end + +try M.Nmax = DCM.M.Nmax; catch, M.Nmax = DCM.options.maxit; end + +% check max nodes +try + DCM.options.maxnodes; +catch + if isfield(DCM.options,'nmax') + DCM.options.maxnodes = DCM.options.nmax; + warning('options.nmax is deprecated. Please use options.maxnodes'); + else + DCM.options.maxnodes = 8; + end +end % analysis and options %-------------------------------------------------------------------------- @@ -172,7 +200,8 @@ % priors (and initial states) %-------------------------------------------------------------------------- [pE,pC,x] = spm_dcm_fmri_priors(DCM.a,DCM.b,DCM.c,DCM.d,DCM.options); -str = 'Using specified priors\n'; +str = 'Using specified priors '; +str = [str '(any changes to DCM.a,b,c,d will be ignored)\n']; try, M.P = DCM.options.P; end % initial parameters try, pE = DCM.options.pE; fprintf(str); end % prior expectation @@ -185,13 +214,13 @@ % eigenvector constraints on pC for large models %-------------------------------------------------------------------------- -if n > DCM.options.nmax +if n > DCM.options.maxnodes % remove confounds and find principal (nmax) modes %---------------------------------------------------------------------- y = Y.y - Y.X0*(pinv(Y.X0)*Y.y); V = spm_svd(y'); - V = V(:,1:DCM.options.nmax); + V = V(:,1:DCM.options.maxnodes); % remove minor modes from priors on A %---------------------------------------------------------------------- @@ -203,13 +232,12 @@ % hyperpriors over precision - expectation and covariance %-------------------------------------------------------------------------- -hE = sparse(n,1) + 6; -hC = speye(n,n)/128; +hE = sparse(n,1) + DCM.options.hE; +hC = speye(n,n) * DCM.options.hC; i = DCM.options.hidden; hE(i) = -4; hC(i,i) = exp(-16); - % complete model specification %-------------------------------------------------------------------------- M.f = 'spm_fx_fmri'; % equations of motion @@ -269,7 +297,7 @@ DEM.M(1).E.s = 1/2; % smoothness of fluctuations DEM.M(1).E.d = 2; % embedding dimension DEM.M(1).E.n = 4; % embedding dimension - DEM.M(1).E.nN = DCM.options.nN; % maximum number of iterations + DEM.M(1).E.nN = DCM.options.maxit; % maximum number of iterations % adjust M.f (DEM works in time bins not seconds) and initialize M.P % --------------------------------------------------------------------- diff --git a/spm_dcm_fit.m b/spm_dcm_fit.m index 4ee629d3..c3395428 100644 --- a/spm_dcm_fit.m +++ b/spm_dcm_fit.m @@ -7,7 +7,7 @@ % DCM - Inverted (1st level) DCM structures with posterior densities %__________________________________________________________________________ % -% This routine is just a wrapper that calls the apprioriate dcm inversion +% This routine is just a wrapper that calls the appropriate dcm inversion % routine for a set a pre-specifed DCMs. % % If called with a cell array, each column is assumed to contain 1st level @@ -17,7 +17,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_fit.m 6348 2015-02-25 13:34:21Z peter $ +% $Id: spm_dcm_fit.m 6587 2015-11-02 10:29:49Z karl $ % get filenames and set up @@ -55,12 +55,24 @@ end +elseif isfield(DCM,'MDP') + + % assume the model is specified explicitly + %---------------------------------------------------------------------- + model = 'MDP'; + elseif isfield(DCM.M,'IS') % assume the model is specified explicitly %---------------------------------------------------------------------- model = 'NLSI'; +elseif isfield(DCM.M,'E') + + % assume the model is a hierarchical dynamic model + %---------------------------------------------------------------------- + model = 'DEM'; + else warning('unknown inversion scheme'); @@ -68,30 +80,40 @@ end +% get data structure for each subject (column) +%------------------------------------------------------------------ +for i = 1:Ns + for j = 2:Nm + switch model + + case{'DEM'} + P{i, j}.xY = P{i, 1}.Y; + otherwise + P{i, j}.xY = P{i, 1}.xY; + end + end +end + +%matlabpool open 7 + % loop over subjects (columns) %-------------------------------------------------------------------------- -for i = 1:Ns +%parfor i = 1:numel(P) + +for i = 1:numel(P) % loop over models (rows) %---------------------------------------------------------------------- - for j = 1:Nm - - % Get model specification - %------------------------------------------------------------------ - try, load(P{i,j}); catch, DCM = P{i,j}; end - - % get data structure for this subject (column) - %------------------------------------------------------------------ - if j == 1 - xY = DCM.xY; - else - DCM.xY = xY; - end + + % Get model specification + %------------------------------------------------------------------ + try, load(P{i}); catch, DCM = P{i}; end + + + % invert and save + %================================================================== + switch model - % invert and save - %================================================================== - switch model - % fMRI model %-------------------------------------------------------------- case{'fMRI'} @@ -132,6 +154,11 @@ case{'NFM'} DCM = spm_dcm_nfm(DCM); + % behavioural Markov decision process model + %---------------------------------------------------------- + case{'MDP'} + DCM = spm_dcm_mdp(DCM); + % generic nonlinear system identification %---------------------------------------------------------- case{'NLSI'} @@ -141,14 +168,23 @@ DCM.Cp = Cp; DCM.F = F; - otherwise - spm('alert!','unknown DCM','Warning'); - return - end - - % place inverted model in output array - %------------------------------------------------------------------ - P{i,j} = DCM; - + % hierarchical ddynamic mmodel + %---------------------------------------------------------- + case{'DEM'} + DCM = spm_DEM(DCM); + + otherwise + try + DCM = feval(model, DCM); + catch + error('unknown DCM'); + end end + + % place inverted model in output array + %------------------------------------------------------------------ + P{i} = DCM; end + + +%matlabpool close \ No newline at end of file diff --git a/spm_dcm_fmri_csd.m b/spm_dcm_fmri_csd.m index 0e010dd4..8ff773f6 100644 --- a/spm_dcm_fmri_csd.m +++ b/spm_dcm_fmri_csd.m @@ -23,7 +23,7 @@ % spectra are the predicted spectra plus some smooth Gaussian fluctuations % (noise). The characterisation of the model parameters can then be % examined in terms of directed transfer functions, spectral density and -% crosscorrelation functions at the neuronal level – having accounted for +% crosscorrelation functions at the neuronal level - having accounted for % variations in haemodynamics at each node. % % note that neuronal fluctuations are not changes in synaptic activity or @@ -33,12 +33,12 @@ % % see also: spm_dcm_estimate %__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2013-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_fmri_csd.m 6303 2015-01-14 10:09:38Z adeel $ +% $Id: spm_dcm_fmri_csd.m 6662 2016-01-08 15:20:02Z adeel $ -SVNid = '$Rev: 6303 $'; +SVNid = '$Rev: 6662 $'; % Load DCM structure %-------------------------------------------------------------------------- @@ -59,13 +59,14 @@ try, DCM.options.centre; catch, DCM.options.centre = 0; end try, DCM.options.nmax; catch, DCM.options.nmax = 8; end try, DCM.options.analysis; catch, DCM.options.analysis = 'CSD'; end +try, DCM.options.Fdcm; catch, DCM.options.Fdcm = [1/128 0.1]; end try, DCM.options.nograph; catch, DCM.options.nograph = spm('CmdLine'); end % parameter initialisation %-------------------------------------------------------------------------- try, DCM.M.P = DCM.options.P; end -try, DCM.M.Nmax = DCM.options.Nmax; catch, DCM.M.Nmax = 32; end +try, DCM.M.Nmax = DCM.options.Nmax; catch, DCM.M.Nmax = 128; end % sizes %-------------------------------------------------------------------------- @@ -102,7 +103,7 @@ DCM.d = zeros(n,n,0); if isempty(DCM.c) || isempty(DCM.U.u) DCM.c = zeros(DCM.n,1); - DCM.b = zeros(DCM.n,DCM.n,1); + DCM.b = zeros(DCM.n,DCM.n,1); DCM.U.u = zeros(DCM.v,1); DCM.U.name = {'null'}; end @@ -148,8 +149,8 @@ DCM.M.x = x; DCM.M.pE = pE; DCM.M.pC = pC; -DCM.M.hE = 4; -DCM.M.hC = 1/128; +DCM.M.hE = 8; +DCM.M.hC = 1/256; DCM.M.n = length(spm_vec(x)); DCM.M.m = size(DCM.U.u,2); DCM.M.l = n; @@ -226,9 +227,9 @@ Qp.b = Qp.b - 32; % Switch off noise Qp.c = Qp.c - 32; % Switch off noise Qp.C = Ep.C; -[Hs Hz dtf] = spm_csd_fmri_mtf(Qp,M,DCM.U); -[ccf pst] = spm_csd2ccf(Hs,Hz); -[coh fsd] = spm_csd2coh(Hs,Hz); +[Hs,Hz,dtf] = spm_csd_fmri_mtf(Qp,M,DCM.U); +[ccf,pst] = spm_csd2ccf(Hs,Hz); +[coh,fsd] = spm_csd2coh(Hs,Hz); DCM.dtf = dtf; DCM.ccf = ccf; DCM.coh = coh; diff --git a/spm_dcm_fmri_csd_DEM.m b/spm_dcm_fmri_csd_DEM.m index 1490bcb8..8b546fee 100644 --- a/spm_dcm_fmri_csd_DEM.m +++ b/spm_dcm_fmri_csd_DEM.m @@ -23,7 +23,7 @@ % spectra are the predicted spectra plus some smooth Gaussian fluctuations % (noise). The characterisation of the model parameters can then be % examined in terms of directed transfer functions, spectral density and -% crosscorrelation functions at the neuronal level – having accounted for +% crosscorrelation functions at the neuronal level - having accounted for % variations in haemodynamics at each node. % % This scheming uses a hierarchical generative model of connectivity with @@ -37,10 +37,10 @@ % see also: spm_dcm_estimate % spm_dcm_fmri_csd %__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2013-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_fmri_csd_DEM.m 5821 2013-12-31 14:26:41Z karl $ +% $Id: spm_dcm_fmri_csd_DEM.m 6656 2015-12-24 16:49:52Z guillaume $ % get DCM @@ -278,9 +278,9 @@ Qp.b = Qp.b - 32; % Switch off noise Qp.c = Qp.c - 32; % Switch off noise Qp.C = Ep.C; -[Hs Hz dtf] = spm_csd_fmri_mtf(Qp,M,DCM.U); -[ccf pst] = spm_csd2ccf(Hs,Hz); -[coh fsd] = spm_csd2coh(Hs,Hz); +[Hs,Hz,dtf] = spm_csd_fmri_mtf(Qp,M,DCM.U); +[ccf,pst] = spm_csd2ccf(Hs,Hz); +[coh,fsd] = spm_csd2coh(Hs,Hz); DCM.dtf = dtf; DCM.ccf = ccf; DCM.coh = coh; diff --git a/spm_dcm_fmri_results.m b/spm_dcm_fmri_csd_results.m similarity index 94% rename from spm_dcm_fmri_results.m rename to spm_dcm_fmri_csd_results.m index 25dc4f1d..c6032aa1 100644 --- a/spm_dcm_fmri_results.m +++ b/spm_dcm_fmri_csd_results.m @@ -1,6 +1,6 @@ -function spm_dcm_fmri_results(DCM,action,fig) +function spm_dcm_fmri_csd_results(DCM,action,fig) % Review an estimated DCM for BOLD CSD -% FORMAT spm_dcm_fmri_results(DCM,action,fig) +% FORMAT spm_dcm_fmri_csd_results(DCM,action,fig) % % Action: % 'Spectral data' @@ -17,7 +17,10 @@ function spm_dcm_fmri_results(DCM,action,fig) % 'Location of regions' % 'Quit' %__________________________________________________________________________ -% Copyright (C) 2013-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2013-2016 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_dcm_fmri_csd_results.m 6673 2016-01-12 17:23:32Z guillaume $ %-Input arguments @@ -33,14 +36,14 @@ function spm_dcm_fmri_results(DCM,action,fig) load(DCM); end -%-Get action if neccessary +%-Get action if necessary %-------------------------------------------------------------------------- if nargin < 2 || isempty(action) - str = {'Spectral data',... + str = {'Timeseries data',... + 'Spectral data',... 'Coupling (A)',... 'Coupling (C)',... 'Inputs',... - 'Outputs',... 'Transfer functions',... 'Cross-spectra (BOLD)',... 'Cross-spectra (neural)',... @@ -100,7 +103,22 @@ function spm_dcm_fmri_results(DCM,action,fig) % switch %-------------------------------------------------------------------------- switch lower(action) - + + %====================================================================== + % Timeseries data + %====================================================================== + case lower('Timeseries data') + + % graph + %------------------------------------------------------------------ + x = (1:DCM.v)*DCM.Y.dt; + for i = 1:ns + subplot(ns,1,i); + plot(x,DCM.Y.y(:,i)); + title([DCM.Y.name{i} ': responses'],'FontSize',16); + xlabel('time {seconds}'); + end + %====================================================================== % spectral data %====================================================================== @@ -177,31 +195,18 @@ case lower('Inputs') % inputs %------------------------------------------------------------------ - t = (1:length(DCM.U.u))*DCM.U.dt; - for i = 1:nu - - subplot(nu,1,i) - plot(t,full(DCM.U.u(:,i))) - title(['Input - ' DCM.U.name{i}],'FontSize',16) - ylabel('event density {Hz}') - spm_axis tight - - end - xlabel('time {seconds}') - - %====================================================================== - % Outputs - %====================================================================== - case lower('Outputs') - - % graph - %------------------------------------------------------------------ - x = (1:DCM.v)*DCM.Y.dt; - for i = 1:ns - subplot(ns,1,i); - plot(x,DCM.Y.y(:,i)); - title([DCM.Y.name{i} ': responses'],'FontSize',16); - xlabel('time {seconds}'); + try + t = (1:length(DCM.U.u))*DCM.U.dt; + for i = 1:nu + + subplot(nu,1,i) + plot(t,full(DCM.U.u(:,i))) + title(['Input - ' DCM.U.name{i}],'FontSize',16) + ylabel('event density {Hz}') + spm_axis tight + + end + xlabel('time {seconds}') end %====================================================================== @@ -480,5 +485,5 @@ case lower('Quit') % return to menu %-------------------------------------------------------------------------- if nargin < 2 - spm_dcm_fmri_results(DCM); + spm_dcm_fmri_csd_results(DCM); end diff --git a/spm_dcm_fmri_priors.m b/spm_dcm_fmri_priors.m index 319de444..fe7eb360 100644 --- a/spm_dcm_fmri_priors.m +++ b/spm_dcm_fmri_priors.m @@ -27,7 +27,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_fmri_priors.m 6198 2014-09-25 10:38:48Z karl $ +% $Id: spm_dcm_fmri_priors.m 6560 2015-09-23 13:50:43Z karl $ % number of regions %-------------------------------------------------------------------------- @@ -122,19 +122,17 @@ % and add hemodynamic priors %========================================================================== -pE.transit = sparse(n,1); pC.transit = sparse(n,1) + exp(-6); -pE.decay = sparse(n,1); pC.decay = sparse(n,1) + exp(-6); -pE.epsilon = sparse(1,1); pC.epsilon = sparse(1,1) + exp(-6); +pE.transit = sparse(n,1); pC.transit = sparse(n,1) + 1/256; +pE.decay = sparse(n,1); pC.decay = sparse(n,1) + 1/256; +pE.epsilon = sparse(1,1); pC.epsilon = sparse(1,1) + 1/256; % add prior on spectral density of fluctuations (amplitude and exponent) %-------------------------------------------------------------------------- if options.induced - - pE.a = sparse(2,n); pC.a = sparse(2,n) + 1/64; % neuronal fluctuations - pE.b = sparse(2,1); pC.b = sparse(2,1) + 1/64; % channel noise global - pE.c = sparse(2,n); pC.c = sparse(2,n) + 1/64; % channel noise specific - + pE.a = sparse(2,n); pC.a = sparse(2,n) + 1/16; % neuronal fluctuations + pE.b = sparse(2,1); pC.b = sparse(2,1) + 1/16; % channel noise global + pE.c = sparse(2,n); pC.c = sparse(2,n) + 1/16; % channel noise specific end % prior covariance matrix diff --git a/spm_dcm_loo.m b/spm_dcm_loo.m index 72753bb8..19b85450 100644 --- a/spm_dcm_loo.m +++ b/spm_dcm_loo.m @@ -1,6 +1,6 @@ -function [qE,qC,Q] = spm_dcm_loo(DCM,X,field) +function [qE,qC,Q] = spm_dcm_loo(DCM,M,field) % Leave-one-out cross validation for empirical Bayes and DCM -% FORMAT [qE,qC,Q] = spm_dcm_loo(DCM,X,field) +% FORMAT [qE,qC,Q] = spm_dcm_loo(DCM,M,field) % % DCM - {N [x M]} structure DCM array of (M) DCMs from (N) subjects % ------------------------------------------------------------------- @@ -9,7 +9,7 @@ % DCM{i}.Ep - posterior expectations % DCM{i}.Cp - posterior covariance % -% X - second level design matrix, where X(:,1) = ones(N,1) [default] +% M.X - second level design matrix, where X(:,1) = ones(N,1) [default] % field - parameter fields in DCM{i}.Ep to optimise [default: {'A','B'}] % 'All' will invoke all fields % @@ -35,7 +35,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_loo.m 6373 2015-03-11 17:10:54Z karl $ +% $Id: spm_dcm_loo.m 6532 2015-08-23 13:59:19Z karl $ % Set up @@ -47,7 +47,10 @@ field = {'A','B'}; end if strcmpi(field,'all'); - field = fieldnames(DCM(1,1).M.pE); + field = fieldnames(DCM{1}.M.pE); +end +if isnumeric(M) + M = struct('X',M); end % Repeat for each column if TEST is an array @@ -57,7 +60,7 @@ % loop over models in each column %---------------------------------------------------------------------- for i = 1:size(DCM,2) - [p,q,r] = spm_dcm_loo(DCM(:,i),X,field); + [p,q,r] = spm_dcm_loo(DCM(:,i),M,field); qE{i} = p; qC{i} = q; Q{i} = r; @@ -74,7 +77,7 @@ %---------------------------------------------------------------------- j = 1:Ns; j(i) = []; - [Ep,Cp,P] = spm_dcm_ppd(DCM(i),DCM(j,1),X(i,:),X(j,:),field); + [Ep,Cp,P] = spm_dcm_ppd(DCM(i),DCM(j,1),M.X(i,:),M.X(j,:),field); qE(i) = Ep; qC(i) = Cp; Q(:,i) = P; @@ -85,30 +88,31 @@ %-------------------------------------------------------------------------- spm_figure('GetWin','LOO cross-validation');clf subplot(2,2,1), spm_plot_ci(qE,qC), hold on -plot(X(:,2),':'), hold off +plot(M.X(:,2),':'), hold off xlabel('subject'), ylabel('group effect') title('Out of sample estimates','FontSize',16) spm_axis tight, axis square % classical inference on classification accuracy %-------------------------------------------------------------------------- -[T,df] = spm_ancova(X(:,1:2),[],qE(:),[0;1]); -r = corr(qE(:),X(:,2)); -p = 1 - spm_Fcdf(T^2,df); +[T,df] = spm_ancova(M.X(:,1:2),[],qE(:),[0;1]); +r = corr(qE(:),M.X(:,2)); +p = 1 - spm_Tcdf(T,df(2)); str = sprintf('corr(df:%-2.0f) = %-0.2f: p = %-0.5f',df(2),r,p); subplot(2,2,2) -plot(X(:,2),qE,'o','Markersize',8) +plot(M.X(:,2),qE,'o','Markersize',8) xlabel('group effect'), ylabel('estimate') title(str,'FontSize',16) -spm_axis tight, set(gca,'XLim',[min(X(:,2))-1 max(X(:,2)+1)]), axis square +axis square if size(Q,1) > 2 - subplot(2,1,2), imagesc(Q) - xlabel('subject'), ylabel('levels of group effect') - title('Posterior probability','FontSize',16) - axis square + subplot(2,1,2), imagesc(1:Ns,unique(M.X(:,2)),Q), hold on + plot(M.X(:,2),'.c','MarkerSize',32), hold off + xlabel('Subject'), ylabel('levels of group effect') + title('Predictive posterior (and true values)','FontSize',16) + axis square xy else subplot(2,1,2), bar(Q(2,:)) xlabel('subject'), ylabel('posterior probability') diff --git a/spm_dcm_mdp.m b/spm_dcm_mdp.m new file mode 100644 index 00000000..4910bcb1 --- /dev/null +++ b/spm_dcm_mdp.m @@ -0,0 +1,141 @@ +function [DCM] = spm_dcm_mdp(DCM) +% MDP inversion using Variational Bayes +% FORMAT [DCM] = spm_dcm_mdp(DCM) +% +% Expects: +%-------------------------------------------------------------------------- +% DCM.MDP % MDP model +% DCM.field % parameter (field) names to optimise +% DCM.U % cell array of outcomes (stimului) +% DCM.Y % cell array of responses (action) +% +% Returns: +%-------------------------------------------------------------------------- +% DCM.M % genertive model (DCM) +% DCM.Ep % Conditional means (structure) +% DCM.Cp % Conditional covariances +% DCM.F % Free-energy bound on log evidence +% +% This routine inverts (cell arrays of) trials specified in terms of the +% stimuli or outcomes and subsequent choices or responses. It first +% computes the prior expectations (and covariances) of the free parameters +% specified by DCM.field. These parameters are log scaling parametersthat +% are applied to the fields of DCM.MDP. +% +% If there is no learning implicit in multi-trial games, only unique +% trials (as specified by the stimuli), are used to generate (subjective) +% posteriors over choice or action. Otherwise, all trials are used in the +% order specified. +% +% The ensuing posture probabilities over choices are used with the +% subjective actions to evaluate their log probability. This is used to +% optimise the NDP parameters using variational class (with numerical +% evaluation of the curvature). +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_dcm_mdp.m 6587 2015-11-02 10:29:49Z karl $ + +% OPTIONS +%-------------------------------------------------------------------------- +ALL = false; + +% set default for precision: (inference – beta and action - alpha) +%-------------------------------------------------------------------------- +if ~isfield(DCM.MDP,'alpha'), DCM.MDP.alpha = 4; end +if ~isfield(DCM.MDP,'beta' ), DCM.MDP.beta = 1; end + +% prior expectations and covariance +%-------------------------------------------------------------------------- +for i = 1:length(DCM.field) + field = DCM.field{i}; + try + param = DCM.MDP.(field); + param = double(~~param); + catch + param = 1; + end + if ALL + pE.(field) = zeros(size(param)); + pC{i,i} = diag(param); + else + pE.(field) = 0; + pC{i,i} = 1; + end +end +pC = spm_cat(pC)/16; + +% model specification +%-------------------------------------------------------------------------- +M.L = @(P,M,U,Y)spm_mdp_L(P,M,U,Y); % log-likelihood function +M.pE = pE; % prior means (parameters) +M.pC = pC; % prior variance (parameters) +M.mdp = DCM.MDP; % MDP structure + +% Variational Laplace +%-------------------------------------------------------------------------- +[Ep,Cp,F] = spm_nlsi_Newton(M,DCM.U,DCM.Y); + +% Store posterior densities and log evidnce (free energy) +%-------------------------------------------------------------------------- +DCM.M = M; +DCM.Ep = Ep; +DCM.Cp = Cp; +DCM.F = F; + + +return + +function L = spm_mdp_L(P,M,U,Y) +% log-likelihood function +% FORMAT L = spm_mdp_L(P,M,U,Y) +% P - parameter structure +% M - generative model +% U - inputs +% Y - observed repsonses +%__________________________________________________________________________ + +if ~isstruct(P); P = spm_unvec(P,M.pE); end + +% multiply parameters in MDP +%-------------------------------------------------------------------------- +mdp = M.mdp; +field = fieldnames(M.pE); +for i = 1:length(field) + if any(ismember(fieldnames(mdp),field{i})) + mdp.(field{i}) = mdp.(field{i}).*exp(P.(field{i})); + else + mdp.(field{i}) = exp(P.(field{i})); + end +end + +% discern whether learning is enabled - and identify unique trials if not +%-------------------------------------------------------------------------- +if any(ismember(fieldnames(mdp),{'a','b','d','c','d','e'})) + j = 1:numel(U); + k = 1:numel(U); +else + % find unique trials (up until the last outcome) + %---------------------------------------------------------------------- + u = spm_cat(U'); + [i,j,k] = unique(u(:,1:(end - 1)),'rows'); +end + + +% place MDP in trial structure +%-------------------------------------------------------------------------- +n = numel(j); +[MDP(1:n)] = deal(mdp); +[MDP.o] = deal(U{j}); + +% solve MDP and accumulate log-likelihood +%-------------------------------------------------------------------------- +MDP = spm_MDP_VB(MDP); +L = 0; +for i = 1:numel(Y); + for j = 1:numel(Y{1}); + L = L + log(MDP(k(i)).P(Y{i}(j),j)); + end +end + diff --git a/spm_dcm_peb.m b/spm_dcm_peb.m index fcaf5f77..bf593138 100644 --- a/spm_dcm_peb.m +++ b/spm_dcm_peb.m @@ -9,12 +9,14 @@ % DCM{i}.M.pC - prior covariances of parameters % DCM{i}.Ep - posterior expectations % DCM{i}.Cp - posterior covariance +% DCM{i}.F - free energy % % M.X - second level design matrix, where X(:,1) = ones(N,1) [default] -% M.bE - second level prior expectation of parameters -% M.bC - second level prior covariances of parameters +% M.pC - second level prior covariances of parameters % M.hE - second level prior expectation of log precisions % M.hC - second level prior covariances of log precisions +% M.bE - third level prior expectation of parameters +% M.bC - third level prior covariances of parameters % % M.Q - covariance components: {'single','fields','all','none'} % M.beta - within:between precision ratio: [default = 16] @@ -76,7 +78,7 @@ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_peb.m 6449 2015-05-24 14:26:59Z karl $ +% $Id: spm_dcm_peb.m 6645 2015-12-12 14:55:22Z karl $ % get filenames and set up @@ -88,6 +90,13 @@ if ischar(P), P = cellstr(P); end if isstruct(P), P = {P}; end +% check for DEM structures +%-------------------------------------------------------------------------- +try + DEM = P; + P = spm_dem2dcm(P); +end + % parameter fields %-------------------------------------------------------------------------- @@ -95,7 +104,10 @@ if nargin < 2 M.X = ones(length(P),1); end -if isempty(M) +if isnumeric(M) + M = struct('X',M); +end +if ~isfield(M,'X') M.X = ones(length(P),1); end if nargin < 3; @@ -122,10 +134,18 @@ % get (first level) densities (summary statistics) %========================================================================== -q = spm_find_pC(DCM.M.pC,DCM.M.pE,field); % parameter indices -Pstr = spm_fieldindices(DCM.M.pE,q); % field names Ns = numel(P); % number of subjects +if isfield(M,'bC') && Ns > 1 + q = spm_find_pC(M.bC,M.bE,field); % parameter indices +else + q = spm_find_pC(DCM,field); % parameter indices +end +Pstr = spm_fieldindices(DCM.M.pE,q); % field names Np = numel(q); % number of parameters +if Np == 1 + Pstr = {Pstr}; +end + for i = 1:Ns % get first(within subject) level DCM @@ -139,17 +159,28 @@ else pC{i} = DCM.M.pC; end + pC{i} = pC{i}; pE{i} = spm_vec(DCM.M.pE); qE{i} = spm_vec(DCM.Ep); qC{i} = DCM.Cp; - + % deal with rank deficient priors + %---------------------------------------------------------------------- + if i == 1 + U = spm_svd(pC{i}(q,q)); + Ne = numel(pE{1}); + else + if numel(pE{i}) ~= Ne + error('Please ensure all DCMs have the same parameterisation'); + end + end + % select parameters in field %---------------------------------------------------------------------- - pE{i} = pE{i}(q); - pC{i} = pC{i}(q,q); - qE{i} = qE{i}(q); - qC{i} = qC{i}(q,q); + pE{i} = U'*pE{i}(q); + pC{i} = U'*pC{i}(q,q)*U; + qE{i} = U'*qE{i}(q); + qC{i} = U'*qC{i}(q,q)*U; % shrink posterior to accommodate inefficient inversions %---------------------------------------------------------------------- @@ -168,32 +199,37 @@ % second level model %-------------------------------------------------------------------------- -if ~isstruct(M), M = struct('X',M); end if isfield(M,'beta'), beta = M.beta; else, beta = 16; end if isfield(M,'Q'), OPTION = M.Q; else, OPTION = 'single'; end if Ns == 1, OPTION = 'no'; end -% get priors (from DCM if necessary) +% get priors (from DCM if necessary) and ensure correct sizes %-------------------------------------------------------------------------- if isfield(M,'bE') M.bE = spm_vec(M.bE); - if size(M.bE,1) > Np, M.bE = M.bE(q); end + if size(M.bE,1) > Np && Ns > 1, M.bE = M.bE(q); end else - M.bE = pE{1}; + M.bE = U*pE{1}; end if isfield(M,'bC') if isstruct(M.bC), M.bC = diag(spm_vec(M.bC)); end - if size(M.bC,1) > Np, M.bC = M.bC(q,q); end + if size(M.bC,1) > Np && Ns > 1, M.bC = M.bC(q,q); end +else + M.bC = U*pC{1}*U'; +end +if isfield(M,'pC') + if isstruct(M.pC), M.pC = diag(spm_vec(M.pC)); end + if size(M.pC,1) > Np && Ns > 1, M.pC = M.pC(q,q); end else - M.bC = pC{1}; + M.pC = M.bC/beta; end % prior precision (pP) and components (Q) for empirical covariance %-------------------------------------------------------------------------- -pP = spm_inv(M.bC); -pQ = pP*beta; +pP = spm_inv(U'*M.bC*U); +pQ = spm_inv(U'*M.pC*U); Q = {}; switch OPTION @@ -210,6 +246,7 @@ j = find(ismember(q,j)); Q{i} = sparse(Np,Np); Q{i}(j,j) = pQ(j,j); + Q{i} = U'*Q{i}*U; end case{'all'} @@ -217,6 +254,7 @@ %------------------------------------------------------------------ for i = 1:Np Q{i} = sparse(i,i,pQ(i,i),Np,Np); + Q{i} = U'*Q{i}*U; end otherwise @@ -230,7 +268,7 @@ % between-subject design matrices and prior expectations %====================================================================== X = M.X; - W = speye(Np,Np); + W = U'*speye(Np,Np)*U; else @@ -254,20 +292,21 @@ gC = 1/16; try, gE = M.hE; end try, gC = M.hC; end -try, bX = M.bX; catch +try, Xc = M.bX; catch % adjust (second level) priors for the norm of explanatory variables %---------------------------------------------------------------------- - bX = diag(size(X,1)./sum(X.^2)); + Xc = diag(size(X,1)./sum(X.^2)); end % prior expectations and precisions of second level parameters %-------------------------------------------------------------------------- -bE = kron(spm_speye(Nx,1),M.bE); % prior expectation of group effects -gE = zeros(Ng,1) + gE; % prior expectation of log precisions -bC = kron(bX,M.bC); % prior covariance of group effects -gC = eye(Ng,Ng)*gC; % prior covariance of log precisions +Xe = spm_speye(Nx,1); +bE = kron(Xe,U'*M.bE); % prior expectation of group effects +gE = zeros(Ng,1) + gE; % prior expectation of log precision +bC = kron(Xc,U'*M.bC*U); % prior covariance of group effects +gC = eye(Ng,Ng)*gC; % prior covariance of log precision bP = spm_inv(bC); gP = spm_inv(gC); @@ -280,13 +319,13 @@ % variational Laplace %-------------------------------------------------------------------------- -t = -2; % Fisher scoring parameter -for n = 1:32 +t = -4; % Fisher scoring parameter +for n = 1:64 - % compute prior covariance + % compute prior precision (with a lower bound of pP/256) %---------------------------------------------------------------------- if Ng > 0 - rP = pP; + rP = pP/256; for i = 1:Ng rP = rP + exp(g(i))*Q{i}; end @@ -311,13 +350,13 @@ % get empirical prior expectations and reduced 1st level posterior %------------------------------------------------------------------ Xi = kron(X(i,:),W); - rE = Xi*b; - [Fi,sE,sC] = spm_log_evidence_reduce(qE{i},qC{i},pE{i},pC{i},rE,rC); + rE{i} = Xi*b; + [Fi,sE,sC] = spm_log_evidence_reduce(qE{i},qC{i},pE{i},pC{i},rE{i},rC); % supplement gradients and curvatures %------------------------------------------------------------------ F = F + Fi + iF(i); - dE = sE - rE; + dE = sE - rE{i}; dFdb = dFdb + Xi'*rP*dE; dFdbb = dFdbb + Xi'*(rP*sC*rP - rP)*Xi; for j = 1:Ng @@ -360,11 +399,11 @@ % if F is increasing save current expansion point %---------------------------------------------------------------------- - if F >= F0 && isempty(find(Fb/Nb > 64,1)) + if F >= F0 dF = F - F0; F0 = F; - save tmp b g F0 dFdb dFdbb dFdg dFdgg + save('tmp','b','g','F0','dFdb','dFdbb','dFdg','dFdgg'); % decrease regularisation %------------------------------------------------------------------ @@ -375,7 +414,7 @@ % otherwise, retrieve expansion point and increase regularisation %------------------------------------------------------------------ t = t - 1; - load tmp + load('tmp') end @@ -389,8 +428,13 @@ % Convergence %====================================================================== - fprintf('VL Iteration %-8d: F = %-3.2f dF: %2.4f [%+2.2f]\n',n,full(F),full(dF),t); - if t < -4 || (dF < 1e-4 && n > 4), break, end + if ~isfield(M,'noplot') + fprintf('VL Iteration %-8d: F = %-3.2f dF: %2.4f [%+2.2f]\n',n,full(F),full(dF),t); + end + if t < -4 || (dF < 1e-4 && n > 4) + fprintf('VL Iteration %-8d: F = %-3.2f dF: %2.4f [%+2.2f]\n',n,full(F),full(dF),t); + break + end end @@ -432,11 +476,11 @@ % augment empirical priors %------------------------------------------------------------------ RP = spm_inv(pC{i}); - RP(q,q) = rP; + RP(q,q) = U*rP*U'; RC = spm_inv(RP); RE = spm_vec(pE{i}); - RE(q) = rE; + RE(q) = U*rE{i}; RE = spm_unvec(RE,pE{i}); % First level BMR (supplemented with second level complexity) @@ -461,21 +505,27 @@ PEB.Pnames = Pstr'; PEB.Pind = q; +Ub = kron(eye(Nx,Nx),U); PEB.M.X = X; PEB.M.W = W; -PEB.M.pE = bE; -PEB.M.pC = bC; +PEB.M.U = U; +PEB.M.pE = kron(Xe,M.bE); +PEB.M.pC = kron(Xc,M.bC); PEB.M.hE = gE; PEB.M.hC = gC; -PEB.Ep = reshape(b,size(W,2),size(X,2)); +PEB.Ep = U*reshape(b,Nw,Nx); PEB.Eh = g; -PEB.Cp = Cb; PEB.Ch = Cg; -PEB.Cph = Cp; -PEB.Ce = rC; +PEB.Cp = Ub*Cb*Ub'; +PEB.Ce = U*rC*U'; PEB.F = F; try, delete tmp.mat, end +% check for DEM structures +%-------------------------------------------------------------------------- +try, P = spm_dem2dcm(DEM,P); end + + diff --git a/spm_dcm_peb_bmc.m b/spm_dcm_peb_bmc.m index ea13eace..9788d78e 100644 --- a/spm_dcm_peb_bmc.m +++ b/spm_dcm_peb_bmc.m @@ -49,14 +49,11 @@ % (encoded by the design matrix, X). Using Bayesian model reduction, this % joint model space is scored over the specified models at the first level % (for the constant terms modelling effects that are common to all -% subjects) and combinations of group effect (generally modelling between -% subject differences). This model comparison is in terms of second level -% parameters; however, if a parameter is removed at the second level it is -% alos removed from the first (i.e., if the group averge is zero, then it -% is zero for all subjects). +% subjects) and combinations of group effects (modelling between +% subject differences). % % If there is only a group effect (and no between subject differences) this -% reduces to a search over different models at the first level. +% reduces to a search over different models of the group mean. % % Given the model space one can then computes the posterior probability % of various combinations of group effects over different parameters. Of @@ -79,7 +76,7 @@ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_peb_bmc.m 6466 2015-06-03 12:42:14Z karl $ +% $Id: spm_dcm_peb_bmc.m 6587 2015-11-02 10:29:49Z karl $ % (greedy) search over all combinations of second level parameters %========================================================================== @@ -89,13 +86,13 @@ %---------------------------------------------------------------------- BMA = spm_dcm_bmr_all(PEB); - % plot posteriors over parameters + % plot posteriors over parameters %====================================================================== spm_figure('Getwin','BMC'); clf Np = numel(PEB.Pind); - str = {'Group means','Group effect'}; - Nx = min(2,size(PEB.M.X,2)); + Nx = min(3,size(PEB.M.X,2)); + str = {'Group mean','1st group effect','2nd group effect'}; for i = 1:Nx j = (1:Np)' + (i - 1)*Np; @@ -106,7 +103,7 @@ title(str{i},'FontSize',16) xlabel('Parameter','FontSize',12) ylabel('Effect size','FontSize',12) - axis square + axis square, a = axis; % posterior density over parameters %------------------------------------------------------------------ @@ -114,11 +111,16 @@ title('Reduced','FontSize',16) xlabel('Parameter','FontSize',12) ylabel('Effect size','FontSize',12) - axis square + axis square, axis(a); % posterior density over parameters %------------------------------------------------------------------ - subplot(3,Nx,Nx + Nx + i), bar(diag(BMA.Pp(j)),Np) + subplot(3,Nx,Nx + Nx + i) + if Np > 1 + bar(diag(BMA.Pp(j)),Np) + else + bar(BMA.Pp(j)) + end title('Posterior','FontSize',16) xlabel('Parameter','FontSize',12) ylabel('Probability','FontSize',12) @@ -154,7 +156,7 @@ Np = length(PEB.Pind); K = ones(Nm,Np); for i = 1:Nm - k = spm_find_pC(models{i}.M.pC); + k = spm_find_pC(models{i}); j = find(~ismember(PEB.Pind,k)); K(i,j) = 0; end @@ -190,11 +192,10 @@ % Get priors and posteriors - of first and second order parameters %-------------------------------------------------------------------------- -qE = spm_vec(PEB.Ep,PEB.Eh); -qC = PEB.Cph; -pE = spm_vec(PEB.M.pE,PEB.M.hE); -pC = blkdiag(PEB.M.pC,PEB.M.hC); -q = 1:Nx*Np; +qE = spm_vec(PEB.Ep); +qC = PEB.Cp; +pE = spm_vec(PEB.M.pE); +pC = PEB.M.pC; for i = 1:Nm if Nx > 1 @@ -207,14 +208,14 @@ %-------------------------------------------------------------- k = [K(i,:) K(j,:) ones(1,(Nx - 2)*Np)]; R = diag(k); - rE = spm_vec(R*PEB.M.pE, PEB.M.hE); - rC = blkdiag(R*PEB.M.pC*R,PEB.M.hC); + rE = R*pE; + rC = R*pC*R; % Bayesian model reduction (of second level) %-------------------------------------------------------------- [F, sE, sC] = spm_log_evidence_reduce(qE,qC,pE,pC,rE,rC); - BMR{i,j}.Ep = sE(q); - BMR{i,j}.Cp = sC(q,q); + BMR{i,j}.Ep = sE; + BMR{i,j}.Cp = sC; BMR{i,j}.F = F; G(i,j) = F; @@ -231,14 +232,14 @@ k = K(i,:); R = diag(k); - rE = spm_vec(R*PEB.M.pE, PEB.M.hE); - rC = blkdiag(R*PEB.M.pC*R,PEB.M.hC); + rE = R*pE; + rC = R*pC*R; % Bayesian model reduction (of second level) %------------------------------------------------------------------ [F, sE, sC] = spm_log_evidence_reduce(qE,qC,pE,pC,rE,rC); - BMR{i,1}.Ep = sE(q); - BMR{i,1}.Cp = sC(q,q); + BMR{i,1}.Ep = sE; + BMR{i,1}.Cp = sC; BMR{i,1}.F = F; G(i,1) = F; end diff --git a/spm_dcm_peb_fit.m b/spm_dcm_peb_fit.m index a31f673d..f7dac591 100644 --- a/spm_dcm_peb_fit.m +++ b/spm_dcm_peb_fit.m @@ -70,7 +70,7 @@ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_peb_fit.m 6449 2015-05-24 14:26:59Z karl $ +% $Id: spm_dcm_peb_fit.m 6532 2015-08-23 13:59:19Z karl $ % set up @@ -91,7 +91,7 @@ % get priors %-------------------------------------------------------------------------- DCM = GCM(:,1); -[i,rC,rE] = spm_find_pC(DCM{1}); +[~,rC,rE] = spm_find_pC(DCM{1}); % priors and parameter fields %-------------------------------------------------------------------------- @@ -147,7 +147,7 @@ disp('free energy : ');disp(F); disp('conditional entropy: ');disp(H); - if k > 2 + if k > 1 if H(k) > H(k - 1) DCM = tmpDCM; PEB = tmpPEB; diff --git a/spm_dcm_peb_full.m b/spm_dcm_peb_full.m index 9e843ac0..53833ef7 100644 --- a/spm_dcm_peb_full.m +++ b/spm_dcm_peb_full.m @@ -52,7 +52,7 @@ % over the parameters of a set of first level DCMs, using second level or % between subject constraints specified in the design matrix X. This scheme % is efficient in the sense that it does not require inversion of the first -% level DCMs � it just requires the prior and posterior densities from each +% level DCMs - it just requires the prior and posterior densities from each % first level DCMs to compute empirical priors under the implicit % hierarchical model. The output of this scheme (PEB) can be re-entered % recursively to invert deep hierarchical models. Furthermore, Bayesian @@ -73,7 +73,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_peb_full.m 6427 2015-05-05 15:42:35Z karl $ +% $Id: spm_dcm_peb_full.m 6501 2015-07-17 14:32:09Z spm $ % get filenames and set up @@ -454,5 +454,3 @@ PEB.F = F; try, delete tmp.mat, end - - diff --git a/spm_dcm_peb_rnd.m b/spm_dcm_peb_rnd.m index ecf89a0f..a518e24d 100644 --- a/spm_dcm_peb_rnd.m +++ b/spm_dcm_peb_rnd.m @@ -1,6 +1,6 @@ -function [p] = spm_dcm_peb_rnd(DCM,M,field) +function [p,P,f,F,X] = spm_dcm_peb_rnd(DCM,M,field) % Re-randomisation testing for empirical Bayes and DCM -% FORMAT [p] = spm_dcm_peb_rnd(DCM,M,field) +% FORMAT [p,P,f,F,X] = spm_dcm_peb_rnd(DCM,M,field) % % DCM - {N x 1} structure DCM array of (M) DCMs from (N) subjects % ------------------------------------------------------------------- @@ -16,10 +16,16 @@ % M.hC - second level prior covariances of log precisions % M.Q - covariance components: {'single','fields','all','none'} % -% field - parameter fields in DCM{i}.Ep to optimise [default: {'A','B'}] -% 'All' will invoke all fields +% M.N - number of re-randomizations [default: M.N = 32] +% +% field - parameter fields in DCM{i}.Ep to optimise [default: {'A','B'}] +% 'All' will invoke all fields % -% p - classical (re-randomization) p - value +% p - classical (re-randomization) p-value +% P - null distribution of p-values +% f - Bayesian (posterior) p-value +% F - null distribution of log Bayes factors +% X - randomised design generating non-distribution %__________________________________________________________________________ % % This routine uses the posterior density over the coefficients of @@ -35,14 +41,21 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_peb_rnd.m 6385 2015-03-21 12:06:22Z karl $ +% $Id: spm_dcm_peb_rnd.m 6557 2015-09-20 12:44:30Z karl $ % Set up %========================================================================== +% supress plotting +%-------------------------------------------------------------------------- +if nargout, M.noplot = 1; end + % parameter fields %-------------------------------------------------------------------------- +if ~isfield(M,'N') + M.N = 32; +end if nargin < 3; field = {'A','B'}; end @@ -51,38 +64,48 @@ end + % re-randomisation %-------------------------------------------------------------------------- Ns = size(M.X,1); -N = 32; +N = M.N; M0 = M; for i = 1:N M0.X(:,2) = M.X(randperm(Ns),2); bmc = spm_dcm_bmc_peb(DCM,M0,field); F(i,:) = bmc.F; + X(:,i) = M0.X(:,2); end -j = 1 + size(bmc.K,1)/2; -F = F(:,1) - F(:,j); +j = find(bmc.K(:,2)); +P = spm_softmax(F'); +P = sum(P(j,:),1); +F = log(P./(1 - P)); % Bayesian model comparison %-------------------------------------------------------------------------- BMC = spm_dcm_bmc_peb(DCM,M,field); -G = BMC.F(1) - BMC.F(j); +G = BMC.F; +f = spm_softmax(G'); +f = sum(f(j,:),1); +G = log(f/(1 - f)); p = (sum(F > G) + 1)/(N + 1); r = sort(F); r = r(fix((1 - 0.05)*N)); +if nargout, return, end + % show results %-------------------------------------------------------------------------- spm_figure('GetWin','PEB-BMC'); -subplot(3,2,1) -hist(F,32), hold on -plot([G G],[0 N/4],'--r'), hold on -plot([r r],[0 N/4],'--b'), hold on -text(G,N/6,sprintf('p < %-2.3f',p), 'FontSize',10), hold off -text(r,N/6,sprintf('p < %-2.3f',0.05),'FontSize',10), hold off +subplot(3,2,1),hold off +hist(F(isfinite(F))), hold on +YLim = get(gca,'YLim'); +plot([G G],[0 YLim(2)],'r'), hold on +plot([r r],[0 YLim(2)],':r'), hold on +text(G,YLim(2)*3/4,sprintf('p < %-2.3f',p), 'FontSize',10), hold on +text(r,YLim(2)/2 ,sprintf('p < %-2.3f',0.05),'FontSize',10), hold off xlabel('Log Bayes Factor'), ylabel('Frequency') title('Null distribution','FontSize',16) axis square diff --git a/spm_dcm_peb_rnd_search.m b/spm_dcm_peb_rnd_search.m new file mode 100644 index 00000000..99fc7757 --- /dev/null +++ b/spm_dcm_peb_rnd_search.m @@ -0,0 +1,91 @@ +function [BMC,M] = spm_dcm_peb_rnd_search(DCM,M,field) +% Re-randomisation testing for empirical Bayes and DCM +% FORMAT [BMC,M] = spm_dcm_peb_rnd_search(DCM,M,field) +% +% DCM - {N x 1} structure DCM array of (M) DCMs from (N) subjects +% ------------------------------------------------------------------- +% DCM{i}.M.pE - prior expectation of parameters +% DCM{i}.M.pC - prior covariances of parameters +% DCM{i}.Ep - posterior expectations +% DCM{i}.Cp - posterior covariance +% +% M.X - second level design matrix, where X(:,1) = ones(N,1) [default] +% M.pE - second level prior expectation of parameters +% M.pC - second level prior covariances of parameters +% M.hE - second level prior expectation of log precisions +% M.hC - second level prior covariances of log precisions +% M.Q - covariance components: {'single','fields','all','none'} +% +% field - parameter fields in DCM{i}.Ep to optimise [default: {'A','B'}] +% 'All' will invoke all fields +% +% BMC - Bayesian model comparison structure +% ------------------------------------------------------------- +% BMC.F - free energy over joint model space +% BMC.P - posterior probability over models +% BMC.Px - posterior probability over 1st level models +% BMC.Pw - posterior probability over 2nd level models +% BMC.M - second level model +% BMC.K - model space +%__________________________________________________________________________ +% +% This routine calls spm_dcm_peb_rnd to assess the distribution of log Bayes +% factors for different hyperpriors on between subject precision. It is +% assumed that the best hyperpriors maximise the entropy of the null +% distribution of ensuing p-values. This type of prior is then used to +% perform Bayesian model comparison. The optimised priors are in the second +% level model (M.hE, M.hC) in the output arguments. +% +% See also: spm_dcm_peb_rnd.m and spm_dcm_loo.m +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_dcm_peb_rnd_search.m 6561 2015-09-23 20:41:57Z karl $ + + +% Set up +%========================================================================== +hE = linspace(-4,2,8); +M.hC = 1/32; +M.N = 64; +bins = 1/40:1/20:1; +for i = 1:numel(hE) + rng('default'); + M.hE = hE(i); + [p,P,f,F] = spm_dcm_peb_rnd(DCM,M,field); + CP(i) = p; + BP(i) = 1 - f; + H(:,i) = hist(P,bins)'; + G(:,i) = F'; +end + +% histogram of p-values and entropy (S) +%-------------------------------------------------------------------------- +H = H/M.N; +S = -sum(H.*log(H + 1e-6)); +[s,i] = max(S); + +% repeat with maximum entropy hyperprior +%-------------------------------------------------------------------------- +M.hE = hE(i); +spm_dcm_peb_rnd(DCM,M,field); + +subplot(3,2,3) +imagesc(hE,bins,H), hold on +plot(hE,H(end,:),'w'), hold on +plot([hE(1) hE(end)],[1 1]/20,':w'), hold off +xlabel('hyperprior'), ylabel('p-value') +title('Null distribution of p-values','FontSize',16) + +subplot(3,2,5) +plot(hE,CP, 'b',[hE(1) hE(end)],[1 1]/20, ':b'), hold on +plot(hE,BP,'-.b',[hE(1) hE(end)],[1 1]*log(20),':r'), hold on +plot(hE,S, 'r',[hE(i) hE(i) ],[0 s], ':r'), hold off +xlabel('hyperprior'), ylabel('p-value and entropy') +title('p-values and entropy','FontSize',16) + +% Bayesian model comparison (at first and second levels) +%-------------------------------------------------------------------------- +BMC = spm_dcm_bmc_peb(DCM,M,field); + diff --git a/spm_dcm_peb_test.m b/spm_dcm_peb_test.m new file mode 100644 index 00000000..81e6d929 --- /dev/null +++ b/spm_dcm_peb_test.m @@ -0,0 +1,108 @@ +function [BMC,M] = spm_dcm_peb_test(DCM,M,field) +% BMC over first and second level models with classical hyperpriors +% FORMAT [BMC,M] = spm_dcm_peb_test(DCM,M,field) +% +% DCM - {N x 1} structure DCM array of (M) DCMs from (N) subjects +% ------------------------------------------------------------------- +% DCM{i}.M.pE - prior expectation of parameters +% DCM{i}.M.pC - prior covariances of parameters +% DCM{i}.Ep - posterior expectations +% DCM{i}.Cp - posterior covariance +% +% M.X - second level design matrix, where X(:,1) = ones(N,1) [default] +% M.pE - second level prior expectation of parameters +% M.pC - second level prior covariances of parameters +% M.hE - second level prior expectation of log precisions +% M.hC - second level prior covariances of log precisions +% M.Q - covariance components: {'single','fields','all','none'} +% +% field - parameter fields in DCM{i}.Ep to optimise [default: {'A','B'}] +% 'All' will invoke all fields +% +% BMC - Bayesian model comparison structure +% ------------------------------------------------------------- +% BMC.F - free energy over joint model space +% BMC.P - posterior probability over models +% BMC.Px - posterior probability over 1st level models +% BMC.Pw - posterior probability over 2nd level models +% BMC.M - second level model +% BMC.K - model space +%__________________________________________________________________________ +% +% This routine calls spm_dcm_peb_rnd to assess the distribution of log Bayes +% factors for different hyperpriors on between subject precision. It is +% assumed that the best hyperpriors maximise the entropy of the null +% distribution of ensuing p-values. This hyperprior is then used to +% perform Bayesian model comparison. The optimised priors are in the second +% level model (M.hE, M.hC) in the output arguments. +% +% this (efficient) version simply tracks the base factor of an unlikely +% null model to find the prior expectations of between subject precision +% that renders the Bayes factorconsistent with a classical p-value (i.e., +% resolves Lindley's paradox) +% +% See also: spm_dcm_bmc_peb.m and spm_dcm_loo.m +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_dcm_peb_test.m 6561 2015-09-23 20:41:57Z karl $ + + +% Set up +%========================================================================== +rng('default'); +M.hC = 1/32; +M.N = 64; + +% find randomisation with the largest classical p-value +%-------------------------------------------------------------------------- +[p,P,f,F,X] = spm_dcm_peb_rnd(DCM,M,field); + + +M0 = M; +M0.noplot = 1; +[d,i] = max(F); +M0.X(:,2) = X(:,i); + +% evaluate the Bayes factor over different hyperpriors +%-------------------------------------------------------------------------- +hE = linspace(-4,2,64); +for i = 1:numel(hE) + M0.hE = hE(i); + bmc = spm_dcm_bmc_peb(DCM,M0,field); + G(i,:) = bmc.F; +end + +j = find(bmc.K(:,2)); +P = spm_softmax(G'); +P = sum(P(j,:),1); +G = log(P./(1 - P)); + +% find hyperprior that is consistent with classical inference +%-------------------------------------------------------------------------- +p = 1/M.N; +g = log((1 - p)/p); +[d,i] = find(G > g,1); + +% repeat with maximum entropy hyperprior +%-------------------------------------------------------------------------- +if isempty(i) + M.hE = hE(end); +else + M.hE = hE(i); +end + +spm_dcm_peb_rnd(DCM,M,field); + +subplot(3,2,3) +plot(hE,G,'b',[hE(1) hE(end)],[g g],':b',[M.hE M.hE],[min(G) max(G)],'-.') +text(-2,g,sprintf('p < %-2.3f',p),'FontSize',10) +xlabel('Hyperprior'), ylabel('Log-Bayes factor') +title('Threshold under null','FontSize',16) + + +% Bayesian model comparison (at first and second levels) +%-------------------------------------------------------------------------- +BMC = spm_dcm_bmc_peb(DCM,M,field); + diff --git a/spm_dcm_post_hoc.m b/spm_dcm_post_hoc.m index 0cad96a6..9f50bf9c 100644 --- a/spm_dcm_post_hoc.m +++ b/spm_dcm_post_hoc.m @@ -72,10 +72,10 @@ % % See also: spm_dcm_search %__________________________________________________________________________ -% Copyright (C) 2010-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2010-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston, Peter Zeidman -% $Id: spm_dcm_post_hoc.m 6329 2015-02-05 19:25:52Z karl $ +% $Id: spm_dcm_post_hoc.m 6528 2015-08-21 11:48:54Z guillaume $ %-Number of parameters to consider before invoking greedy search @@ -130,29 +130,29 @@ % Check that models are compatible in terms of their prior variances %-------------------------------------------------------------------------- -[example_DCM,C] = check_models(params); +[example_DCM, C] = check_models(params); % Greedy search (GS) - eliminating parameters in a top down fashion %-------------------------------------------------------------------------- -[C,model_space] = greedy_search(C,example_DCM,nmax,params); +[C, model_space] = greedy_search(C, example_DCM, nmax, params); % Inference over families %-------------------------------------------------------------------------- -[Pk, Pf] = family_inference(example_DCM,C,model_space,params); +[Pk, Pf] = family_inference(example_DCM, C, model_space, params); % Calculate reduced models and BPA %-------------------------------------------------------------------------- -[BPA, P_opt] = compute_post_hoc(example_DCM,C,params); +[BPA, P_opt] = compute_post_hoc(example_DCM, C, params); % Show full and reduced conditional estimates (for Bayesian average) %-------------------------------------------------------------------------- if ~params.nograph - try, create_plots(example_DCM,BPA,Pk,Pf,params,~nargout); end + create_plots(example_DCM, BPA, Pk, Pf, params, ~nargout); end % Save Bayesian Parameter Average and family-wise model inference %-------------------------------------------------------------------------- -DCM = save_bpa_dcm(Pk,BPA,Pf,params,P_opt); +DCM = save_bpa_dcm(Pk, BPA, Pf, params, P_opt); %========================================================================== @@ -228,7 +228,7 @@ % Flag a greedy search GS = 1; elseif isempty(k) - error('There are no free parameters in this model'); + error('There are no free parameters in this model.'); else GS = 0; end @@ -244,24 +244,25 @@ %-Show results %---------------------------------------------------------------------- if ~params.nograph - spm_figure('Getwin','Graphics'); clf + Fgraph = spm_figure('Getwin','Graphics'); + spm_figure('Clear',Fgraph); fprintf('%i out of %i free parameters removed \n',nelim,nparam) - subplot(3,2,1) + ax = subplot(3,2,1,'Parent',Fgraph); if length(model_space.K) > 32, plot(model_space.S,'k'),... else bar(model_space.S,'c'), end - title('log-posterior','FontSize',16) - xlabel('model','FontSize',12) - ylabel('log-probability','FontSize',12) - axis square + title(ax,'log-posterior','FontSize',16) + xlabel(ax,'model','FontSize',12) + ylabel(ax,'log-probability','FontSize',12) + axis(ax,'square'); - subplot(3,2,2) + ax = subplot(3,2,2,'Parent',Fgraph); if length(model_space.K) > 32, plot(model_space.p,'k'),... else bar(model_space.p,'r'), end - title('model posterior','FontSize',16) - xlabel('model','FontSize',12) - ylabel('probability','FontSize',12) - axis square + title(ax,'model posterior','FontSize',16) + xlabel(ax,'model','FontSize',12) + ylabel(ax,'probability','FontSize',12) + axis(ax,'square'); drawnow end @@ -630,33 +631,32 @@ function create_plots(DCM,BPA,Pk,Pf,params,extra_plots) %-Plot summary of results %-------------------------------------------------------------------------- -spm_figure('GetWin','Graphics'); - -subplot(3,2,3) -spm_plot_ci(EP(i),BPA.CQ(i,i)) -title('MAP connections (full)','FontSize',16) -axis square -a = axis; - -subplot(3,2,4) -spm_plot_ci(Ep(i),abs(BPA.Cq(i,i))) -title('MAP connections (reduced)','FontSize',16) -axis square -axis(a) - -subplot(3,2,5) -bar(EP(i) - pE(i)) -xlabel('parameter') -title('MAP minus prior','FontSize',16) -spm_axis tight -axis square - -subplot(3,2,6) -bar(Ep(i) - EP(i)) -xlabel('parameter') -title('differences in MAP','FontSize',16) -spm_axis tight -axis square +Fgraph = spm_figure('GetWin','Graphics'); + +ax = subplot(3,2,3,'Parent',Fgraph); +spm_plot_ci(EP(i),BPA.CQ(i,i)); +title(ax,'MAP connections (full)','FontSize',16); +axis(ax,'square'); + +ax = subplot(3,2,4,'Parent',Fgraph); +spm_plot_ci(Ep(i),abs(BPA.Cq(i,i))); +title(ax,'MAP connections (reduced)','FontSize',16); +axis(ax,'square'); + +ax = subplot(3,2,5,'Parent',Fgraph); +bar(ax,EP(i) - pE(i)); +xlabel(ax,'parameter'); +title(ax,'MAP minus prior','FontSize',16); +spm_axis(ax,'tight'); +axis(ax,'square'); + +ax = subplot(3,2,6,'Parent',Fgraph); +bar(ax,Ep(i) - EP(i)); +xlabel(ax,'parameter'); +title(ax,'differences in MAP','FontSize',16); +spm_axis(ax,'tight'); +axis(ax,'square'); + drawnow %-Stop this function here if we only need basic plots @@ -667,26 +667,29 @@ function create_plots(DCM,BPA,Pk,Pf,params,extra_plots) %-Show structural and functional graphs %-------------------------------------------------------------------------- -spm_figure('GetWin','Graph analysis'); clf +F = spm_figure('GetWin','Graph analysis'); +spm_figure('Clear',F); try spm_dcm_graph(DCM.xY,BPA.Eq.A); catch try spm_dcm_graph(DCM,BPA.Eq.A); catch - delete(gcf); + delete(F); end end %-Show coupling matrices %-------------------------------------------------------------------------- -if numel(params.field) == 3 && isfeild(DCM,'a'); +if numel(params.field) == 3 && isfield(DCM,'a') - spm_figure('GetWin','Bayesian parameter average (selected model)'); clf - spm_dcm_fmri_image(BPA.Eq) + F = spm_figure('GetWin','Bayesian parameter average (selected model)'); + spm_figure('Clear',F); + spm_dcm_fmri_image(BPA.Eq); - spm_figure('GetWin','Model posterior (over parameters)'); clf - spm_dcm_fmri_image(Pk) + F = spm_figure('GetWin','Model posterior (over parameters)'); + spm_figure('Clear',F); + spm_dcm_fmri_image(Pk); end @@ -694,12 +697,13 @@ function create_plots(DCM,BPA,Pk,Pf,params,extra_plots) %-------------------------------------------------------------------------- if ~isempty(params.fun) - spm_figure('GetWin','Model posterior (over families)'); clf - subplot(2,1,1) - bar(Pf) - xlabel('familiy') - title('Model posterior (over families)','FontSize',16) - axis square + F = spm_figure('GetWin','Model posterior (over families)'); + ax = subplot(2,1,1,'Parent',F); + bar(ax,Pf); + xlabel(ax,'family'); + title(ax,'Model posterior (over families)','FontSize',16); + axis(ax,'square'); + end @@ -735,9 +739,7 @@ function create_plots(DCM,BPA,Pk,Pf,params,extra_plots) name = fullfile(pwd,'DCM_BPA.mat'); end -if params.write_all - save(name,'DCM', spm_get_defaults('mat.format')); -end +save(name,'DCM', spm_get_defaults('mat.format')); %========================================================================== diff --git a/spm_dcm_ppd.m b/spm_dcm_ppd.m index b997838a..da1a31ab 100644 --- a/spm_dcm_ppd.m +++ b/spm_dcm_ppd.m @@ -38,7 +38,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_ppd.m 6449 2015-05-24 14:26:59Z karl $ +% $Id: spm_dcm_ppd.m 6532 2015-08-23 13:59:19Z karl $ % Set up @@ -79,17 +79,17 @@ % evaluate empirical priors from training set %-------------------------------------------------------------------------- -PEB = spm_dcm_peb(TRAIN,X,field); +M.X = X; +PEB = spm_dcm_peb(TRAIN,M,field); % and estimate their contribution to the test subject %-------------------------------------------------------------------------- nX = size(X,2); % number of explanatory variables -Y(iX) = 0; % prior expectation (variables) bC = var(X(:,iX))*4; bC = sparse(iX,1,bC,nX,1); % prior covariances (variables) M.X = PEB.Ep; % emprical prior expectations M.rP = spm_inv(PEB.Ce); % emprical prior precision (parameters) -M.bE = Y; % prior expectation (variables) +M.bE = Y; M.bE(:,iX) = 0; % prior expectation (variables) M.bC = diag(bC + 1); % prior covariances (variables) for i = 1:4 @@ -105,7 +105,7 @@ % Bayesian model reduction over levels of (second) explanatory variables %-------------------------------------------------------------------------- -x = unique(X(:,iX)); +x = unique([X(:,iX); Y(:,iX)]); for j = 1:length(x) F(j,1) = spm_log_evidence(qE,qC,pE,pC,x(j),0); end diff --git a/spm_dcm_review.m b/spm_dcm_review.m index 1fd57041..cf17b951 100644 --- a/spm_dcm_review.m +++ b/spm_dcm_review.m @@ -19,7 +19,7 @@ function spm_dcm_review(DCM,action) % Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_review.m 6458 2015-05-27 16:22:09Z spm $ +% $Id: spm_dcm_review.m 6671 2016-01-12 12:01:21Z peter $ %-Get DCM structure @@ -32,7 +32,7 @@ function spm_dcm_review(DCM,action) load(DCM); end -%-Call spm_dcm_fmri_results for DCM of CSD +%-Call spm_dcm_fmri_csd_results for DCM of CSD %-------------------------------------------------------------------------- try analysis = DCM.options.analysis; @@ -40,7 +40,7 @@ function spm_dcm_review(DCM,action) analysis = ''; end if strcmp(analysis,'CSD') - spm_dcm_fmri_results(DCM); + spm_dcm_fmri_csd_results(DCM); return end diff --git a/spm_dcm_search.m b/spm_dcm_search.m index 257ee9bb..c4211961 100644 --- a/spm_dcm_search.m +++ b/spm_dcm_search.m @@ -35,7 +35,7 @@ function spm_dcm_search(P) % Copyright (C) 2008-2011 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_search.m 5392 2013-04-05 19:14:45Z karl $ +% $Id: spm_dcm_search.m 6615 2015-11-30 12:56:02Z peter $ % get filenames %-------------------------------------------------------------------------- @@ -75,23 +75,23 @@ function spm_dcm_search(P) % accumate model in terms of which parameters are free %---------------------------------------------------------------------- - A{j} = DCM.a; - B{j} = DCM.b; - C{j} = DCM.c; - D{j} = DCM.d; + A = DCM.a; + B = DCM.b; + C = DCM.c; + D = DCM.d; % Get full models free parameters %---------------------------------------------------------------------- if j == 1 - a = A{j}; - b = B{j}; - c = C{j}; - d = D{j}; + a = A; + b = B; + c = C; + d = D; else - a = a | A{j}; - b = b | B{j}; - c = c | C{j}; - d = d | D{j}; + a = a | A; + b = b | B; + c = c | C; + d = d | D; end end @@ -119,6 +119,13 @@ function spm_dcm_search(P) load(P{j}); + % Fix for endogenous DCM (to match spm_dcm_estimate) + % --------------------------------------------------------------------- + if isempty(DCM.c) || isempty(U.u) + DCM.c = zeros(DCM.n,1); + DCM.b = zeros(DCM.n,DCM.n,1); + end + % Get model (priors) and evaluate (reduced) free-energy and posteriors % --------------------------------------------------------------------- [rE,rC] = spm_dcm_fmri_priors(DCM.a,DCM.b,DCM.c,DCM.d,DCM.options); diff --git a/spm_defaults.m b/spm_defaults.m index 6dc2051b..42860aff 100644 --- a/spm_defaults.m +++ b/spm_defaults.m @@ -20,10 +20,10 @@ % % ** This file should not be edited ** %__________________________________________________________________________ -% Copyright (C) 1994-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1994-2015 Wellcome Trust Centre for Neuroimaging % SPM -% $Id: spm_defaults.m 6255 2014-11-05 09:55:02Z christophe $ +% $Id: spm_defaults.m 6594 2015-11-06 18:47:05Z guillaume $ global defaults @@ -62,6 +62,7 @@ defaults.stats.fmri.t0 = 8; defaults.stats.fmri.hpf = 128; defaults.stats.fmri.cvi = 'AR(1)'; +defaults.stats.fmri.hrf = [6 16 1 1 6 0 32]; % Mask defaults %========================================================================== @@ -85,13 +86,14 @@ % Filename prefix defaults %========================================================================== -defaults.slicetiming.prefix = 'a'; -defaults.realign.write.prefix = 'r'; -defaults.coreg.write.prefix = 'r'; -defaults.unwarp.write.prefix = 'u'; -defaults.normalise.write.prefix = 'w'; -defaults.smooth.prefix = 's'; -defaults.imcalc.prefix = 'i'; +defaults.slicetiming.prefix = 'a'; +defaults.realign.write.prefix = 'r'; +defaults.coreg.write.prefix = 'r'; +defaults.unwarp.write.prefix = 'u'; +defaults.normalise.write.prefix = 'w'; +defaults.deformations.modulate.prefix = 'm'; +defaults.smooth.prefix = 's'; +defaults.imcalc.prefix = 'i'; % Realignment defaults %========================================================================== diff --git a/spm_deformations.m b/spm_deformations.m index 41597c1c..bec23175 100644 --- a/spm_deformations.m +++ b/spm_deformations.m @@ -1,5 +1,5 @@ function out = spm_deformations(job) -% Various deformation field utilities. +% Various deformation field utilities % FORMAT out = spm_deformations(job) % job - a job created via spm_cfg_deformations.m % out - a struct with fields @@ -8,10 +8,10 @@ % % See spm_cfg_deformations.m for more information. %__________________________________________________________________________ -% Copyright (C) 2005-2013 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2005-2015 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_deformations.m 6137 2014-08-19 12:43:11Z john $ +% $Id: spm_deformations.m 6577 2015-10-15 15:22:11Z volkmar $ [Def,mat] = get_comp(job.comp); @@ -21,15 +21,15 @@ fn = fn{1}; switch fn case 'savedef' - out.def = [out.def, save_def(Def,mat,job.out{i}.(fn))]; + out.def = [out.def; save_def(Def,mat,job.out{i}.(fn))]; case 'pull' - out.warped = [out.warped, pull_def(Def,mat,job.out{i}.(fn))]; + out.warped = [out.warped; pull_def(Def,mat,job.out{i}.(fn))]; case 'push' - out.warped = [out.warped, push_def(Def,mat,job.out{i}.(fn))]; + out.warped = [out.warped; push_def(Def,mat,job.out{i}.(fn))]; case 'surf' - out.surf = [out.surf, surf_def(Def,mat,job.out{i}.(fn))]; + out.surf = [out.surf; surf_def(Def,mat,job.out{i}.(fn))]; case 'savejac' - out.jac = [out.jac, jac_def(Def,mat,job.out{i}.(fn))]; + out.jac = [out.jac; jac_def(Def,mat,job.out{i}.(fn))]; otherwise error('Unknown option'); end @@ -364,7 +364,7 @@ PI = job.fnames; intrp = job.interp; intrp = [intrp*[1 1 1], 0 0 0]; -out = cell(1,numel(PI)); +out = cell(numel(PI),1); if numel(PI)==0, return; end @@ -383,7 +383,7 @@ num = sscanf(num,',%d'); j_range = num(1); end - for j=j_range, + for j=j_range M0 = NI.mat; if ~isempty(NI.extras) && isstruct(NI.extras) && isfield(NI.extras,'mat') @@ -436,11 +436,16 @@ end if sum(job.fwhm.^2)==0 - NO.dat.fname = fullfile(wd,['w' nam ext]); - NO.descrip = sprintf('Warped'); + newprefix = spm_get_defaults('normalise.write.prefix'); + NO.descrip = sprintf('Warped'); else - NO.dat.fname = fullfile(wd,['sw' nam ext]); - NO.descrip = sprintf('Smoothed (%gx%gx%g subopt) warped',job.fwhm); + newprefix = [spm_get_defaults('smooth.prefix') spm_get_defaults('normalise.write.prefix')]; + NO.descrip = sprintf('Smoothed (%gx%gx%g subopt) warped',job.fwhm); + end + if isfield(job,'prefix') && ~isempty(job.prefix) + NO.dat.fname = fullfile(wd,[job.prefix nam ext]); + else + NO.dat.fname = fullfile(wd,[newprefix nam ext]); end dim = size(Def); dim = dim(1:3); @@ -540,7 +545,7 @@ odm = zeros(1,3); oM = zeros(4,4); PI = job.fnames; -out = cell(1,numel(PI)); +out = cell(numel(PI),1); for m=1:numel(PI) % Generate headers etc for output images @@ -550,7 +555,7 @@ j_range = 1:size(NI.dat,4); k_range = 1:size(NI.dat,5); l_range = 1:size(NI.dat,6); - if ~isempty(num), + if ~isempty(num) num = sscanf(num,',%d'); if numel(num)>=1, j_range = num(1); end if numel(num)>=2, k_range = num(2); end @@ -572,22 +577,27 @@ NO.dat.scl_slope = 1.0; NO.dat.scl_inter = 0.0; NO.dat.dtype = 'float32-le'; - if sum(job.fwhm.^2)==0, - NO.dat.fname = fullfile(wd,['mw' nam ext]); - NO.descrip = sprintf('Warped & Jac scaled'); + if sum(job.fwhm.^2)==0 + newprefix = [spm_get_defaults('deformations.modulate.prefix') spm_get_defaults('normalise.write.prefix')]; + NO.descrip = sprintf('Warped & Jac scaled'); else - NO.dat.fname = fullfile(wd,['smw' nam ext]); - NO.descrip = sprintf('Smoothed (%gx%gx%g) warped Jac scaled',job.fwhm); + newprefix = [spm_get_defaults('smooth.prefix') spm_get_defaults('deformations.modulate.prefix') spm_get_defaults('normalise.write.prefix')]; + NO.descrip = sprintf('Smoothed (%gx%gx%g) warped Jac scaled',job.fwhm); end else - if sum(job.fwhm.^2)==0, - NO.dat.fname = fullfile(wd,['w' nam ext]); - NO.descrip = sprintf('Warped'); + if sum(job.fwhm.^2)==0 + newprefix = spm_get_defaults('normalise.write.prefix'); + NO.descrip = sprintf('Warped'); else - NO.dat.fname = fullfile(wd,['sw' nam ext]); - NO.descrip = sprintf('Smoothed (%gx%gx%g opt) warped',job.fwhm); + newprefix = [spm_get_defaults('smooth.prefix') spm_get_defaults('normalise.write.prefix')]; + NO.descrip = sprintf('Smoothed (%gx%gx%g opt) warped',job.fwhm); end end + if isfield(job,'prefix') && ~isempty(job.prefix) + NO.dat.fname = fullfile(wd,[job.prefix nam ext]); + else + NO.dat.fname = fullfile(wd,[newprefix nam ext]); + end NO.dat.dim = [dim NI.dat.dim(4:end)]; NO.dat.offset = 0; % For situations where input .nii images have an extension. NO.mat = mat0; @@ -595,7 +605,7 @@ NO.mat_intent = 'Aligned'; NO.mat0_intent = 'Aligned'; - if isempty(num), + if isempty(num) out{m} = NO.dat.fname; else out{m} = [NO.dat.fname, ',', num2str(num(1))]; @@ -611,7 +621,7 @@ % Loop over volumes within the file %---------------------------------------------------------------------- fprintf('%s',nam); drawnow; - for j=j_range, + for j=j_range % Need to resample the mapping by an affine transform % so that it maps from voxels in the native space image @@ -632,7 +642,7 @@ y = zeros([dm(1:3),3],'single'); for d=1:3 yd = y0(:,:,:,d); - for x3=1:size(y,3), + for x3=1:size(y,3) y(:,:,x3,d) = single(spm_slice_vol(yd,M*spm_matrix([0 0 x3]),dm(1:2),[1 NaN])); end end @@ -642,8 +652,8 @@ oM = M; % Write the warped data for this time point. %------------------------------------------------------------------ - for k=k_range, - for l=l_range, + for k=k_range + for l=l_range f = single(NI.dat(:,:,:,j,k,l)); if isempty(wt) if ~job.preserve diff --git a/spm_dem2dcm.m b/spm_dem2dcm.m new file mode 100644 index 00000000..aede28f8 --- /dev/null +++ b/spm_dem2dcm.m @@ -0,0 +1,78 @@ +function [DCM] = spm_dem2dcm(DEM,DCM) +% reorganisation of posteriors and priors into DCM format +% FORMAT [DCM] = spm_dem2dcm(DEM) +% FORMAT [DEM] = spm_dem2dcm(DEM,DCM) +% +% DEM - structure array (hierarchicial model) +% DCM - structure array (flat model) +% +% ------------------------------------------------------------------------- +% DCM.M.pE - prior expectation of parameters +% DCM.M.pC - prior covariances of parameters +% DCM.Ep - posterior expectations +% DCM.Cp - posterior covariance +% DCM.F - free energy +% +% For hierarchical models (DEM.M) the first level with non-zero prorior +% varaince on the paramters will be extracted. +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_dem2dcm.m 6508 2015-07-25 15:23:25Z karl $ + + +% check for arrays +%-------------------------------------------------------------------------- +if iscell(DEM) + for i = 1:size(DEM,1) + for j = 1:size(DEM,2) + if nargin < 2 + DCM{i,j} = spm_dem2dcm(DEM{i,j}); + else + DCM{i,j} = spm_dem2dcm(DEM{i,j},DCM{i,j}); + end + end + end + return +end + + +% get level +%-------------------------------------------------------------------------- +for j = 1:length(DEM.M) + if any(any(DEM.M(j).pC)), break, end +end + +% get indices for covariance +%-------------------------------------------------------------------------- +k = spm_length(DEM.qP.P(j)); +k = spm_length(DEM.qP.P(1:(j - 1))) + (1:k); + +% re-organise +%-------------------------------------------------------------------------- +if nargin < 2 + + DCM.M.pE = DEM.M(j).pE; + DCM.M.pC = DEM.M(j).pC; + DCM.Ep = DEM.qP.P{j}; + DCM.Cp = DEM.qP.C(k,k); + DCM.F = DEM.F(end); + +else + + DEM.M(j).pE = spm_unvec(DCM.M.pE,DEM.M(j).pE); + DEM.M(j).pC = DCM.M.pC; + DEM.qP.P{j} = spm_unvec(DCM.Ep,DEM.qP.P{j}); + DEM.qP.C(k,k) = DCM.Cp; + DEM.F = DCM.F; + + % output argument + %---------------------------------------------------------------------- + DCM = DEM; +end + + + + + diff --git a/spm_dicom_convert.m b/spm_dicom_convert.m index be2a429f..8ea2e46c 100644 --- a/spm_dicom_convert.m +++ b/spm_dicom_convert.m @@ -33,10 +33,10 @@ % cellstring with filenames of created files. If no files are % created, a cell with an empty string {''} is returned. %__________________________________________________________________________ -% Copyright (C) 2002-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2002-2015 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_dicom_convert.m 6455 2015-05-26 12:45:05Z volkmar $ +% $Id: spm_dicom_convert.m 6639 2015-12-11 09:50:49Z volkmar $ %-Input parameters @@ -181,12 +181,18 @@ % Possibly useful information %---------------------------------------------------------------------- - tim = datevec(hdr{i}.AcquisitionTime/(24*60*60)); - descrip = sprintf('%gT %s %s TR=%gms/TE=%gms/FA=%gdeg %s %d:%d:%.5g Mosaic',... - hdr{i}.MagneticFieldStrength, hdr{i}.MRAcquisitionType,... - deblank(hdr{i}.ScanningSequence),... - hdr{i}.RepetitionTime,hdr{i}.EchoTime,hdr{i}.FlipAngle,... - datestr(hdr{i}.AcquisitionDate),tim(4),tim(5),tim(6)); + if checkfields(hdr{i},'AcquisitionTime','MagneticFieldStrength',... + 'MRAcquisitionType','ScanningSequence','RepetitionTime',... + 'EchoTime','FlipAngle','AcquisitionDate') + tim = datevec(hdr{i}.AcquisitionTime/(24*60*60)); + descrip = sprintf('%gT %s %s TR=%gms/TE=%gms/FA=%gdeg %s %d:%d:%.5g Mosaic',... + hdr{i}.MagneticFieldStrength, hdr{i}.MRAcquisitionType,... + deblank(hdr{i}.ScanningSequence),... + hdr{i}.RepetitionTime,hdr{i}.EchoTime,hdr{i}.FlipAngle,... + datestr(hdr{i}.AcquisitionDate),tim(4),tim(5),tim(6)); + else + descrip = hdr{1}.Modality; + end % descrip = [deblank(descrip) ' ' hdr{i}.PatientsName]; @@ -284,7 +290,7 @@ strcmp(vol{j}{1}.Modality,'CT') % Our CT seems to have shears in slice positions dist2 = 0; end - if ~isempty(ice1) && isfield(vol{j}{1},'CSAImageHeaderInfo') && isfield(vol{j}{1}.CSAImageHeaderInfo(1),'name') + if ~isempty(ice1) && isfield(vol{j}{1},'CSAImageHeaderInfo') && numel(vol{j}{1}.CSAImageHeaderInfo)>=1 && isfield(vol{j}{1}.CSAImageHeaderInfo(1),'name') % Replace 'X' in ICE_Dims by '-1' ice2 = sscanf( ... strrep(get_numaris4_val(vol{j}{1}.CSAImageHeaderInfo,'ICE_Dims'), ... @@ -322,6 +328,9 @@ if isfield(hdr{i},'EchoNumbers') && isfield(vol{j}{1}, 'EchoNumbers') match = match && hdr{i}.EchoNumbers == vol{j}{1}.EchoNumbers; end + if isfield(hdr{i},'GE_ImageType') && isfield(vol{j}{1}, 'GE_ImageType') + match = match && hdr{i}.GE_ImageType == vol{j}{1}.GE_ImageType; + end catch match = 0; end @@ -873,7 +882,7 @@ elseif ~checkfields(hdr{i},'StartOfPixelData','SamplesPerPixel',... 'Rows','Columns','BitsAllocated','BitsStored','HighBit','PixelRepresentation') - fprintf('Cant find "Image Pixel" information for "%s".',hdr{i}.Filename); + fprintf('Cant find "Image Pixel" information for "%s".\n',hdr{i}.Filename); guff = [guff(:)',hdr(i)]; elseif ~(checkfields(hdr{i},'PixelSpacing','ImagePositionPatient','ImageOrientationPatient') ... @@ -1048,12 +1057,13 @@ '1.2.840.10008.1.2.4.90','1.2.840.10008.1.2.4.91',... % lossless JPEG 2000 & possibly lossy JPEG 2000, Part 1 '1.2.840.10008.1.2.4.92','1.2.840.10008.1.2.4.93' ... % lossless JPEG 2000 & possibly lossy JPEG 2000, Part 2 }, - % try to read PixelData as JPEG image - offset is just a guess - - fseek(fp,hdr.StartOfPixelData+16,'bof'); - % Skip over the uint16, which seem to encode 65534/57344 (Item), - % followed by 4 0 0 0 and then 65534/57344 (Item) - + % try to read PixelData as JPEG image + fseek(fp,hdr.StartOfPixelData,'bof'); + fread(fp,2,'uint16'); % uint16 encoding 65534/57344 (Item) + offset = double(fread(fp,1,'uint32')); % followed by 4 0 0 0 + fread(fp,2,'uint16'); % uint16 encoding 65534/57344 (Item) + fread(fp,offset); + sz = double(fread(fp,1,'*uint32')); img = fread(fp,sz,'*uint8'); @@ -1211,20 +1221,32 @@ if isfield(hdr,'AcquisitionNumber'), AcquisitionNumber = hdr.AcquisitionNumber; else AcquisitionNumber = 0; end if isfield(hdr,'InstanceNumber'), InstanceNumber = hdr.InstanceNumber; else InstanceNumber = 0; end +ImTyp = ''; +if isfield(hdr,'GE_ImageType') + switch hdr.GE_ImageType + case 1 + ImTyp = '-Phase'; + case 2 + ImTyp = '-Real'; + case 3 + ImTyp = '-Imag'; + end +end + if strcmp(root_dir, 'flat') % Standard SPM file conversion %---------------------------------------------------------------------- if checkfields(hdr,'SeriesNumber','AcquisitionNumber') if checkfields(hdr,'EchoNumbers') - fname = sprintf('%s%s-%.4d-%.5d-%.6d-%.2d.%s', prefix, strip_unwanted(PatientID),... - SeriesNumber, AcquisitionNumber, InstanceNumber, EchoNumbers, format); + fname = sprintf('%s%s-%.4d-%.5d-%.6d-%.2d%s.%s', prefix, strip_unwanted(PatientID),... + SeriesNumber, AcquisitionNumber, InstanceNumber, EchoNumbers, ImTyp, format); else - fname = sprintf('%s%s-%.4d-%.5d-%.6d.%s', prefix, strip_unwanted(PatientID),... - SeriesNumber, AcquisitionNumber, InstanceNumber, format); + fname = sprintf('%s%s-%.4d-%.5d-%.6d%s.%s', prefix, strip_unwanted(PatientID),... + SeriesNumber, AcquisitionNumber, InstanceNumber, ImTyp, format); end else - fname = sprintf('%s%s-%.6d.%s',prefix, ... - strip_unwanted(PatientID),InstanceNumber, format); + fname = sprintf('%s%s-%.6d%s.%s',prefix, ... + strip_unwanted(PatientID), InstanceNumber, ImTyp, format); end fname = fullfile(out_dir,fname); @@ -1286,8 +1308,8 @@ sa = sprintf('%02d', floor(rem(AcquisitionTime,60))); ma = sprintf('%02d', floor(rem(AcquisitionTime/60,60))); ha = sprintf('%02d', floor(AcquisitionTime/3600)); -fname = sprintf('%s%s-%s%s%s-%.5d-%.5d-%d.%s', prefix, id, ha, ma, sa, ... - AcquisitionNumber, InstanceNumber, EchoNumbers, format); +fname = sprintf('%s%s-%s%s%s-%.5d-%.5d-%d%s.%s', prefix, id, ha, ma, sa, ... + AcquisitionNumber, InstanceNumber, EchoNumbers, ImTyp, format); fname = fullfile(dname, fname); @@ -1312,7 +1334,7 @@ function ret = read_ascconv(hdr) % In SIEMENS data, there is an ASCII text section with % additional information items. This section starts with a code -% ### ASCCONV BEGIN ### +% ### ASCCONV BEGIN ### % and ends with % ### ASCCONV END ### % It is read by spm_dicom_headers into an entry 'MrProtocol' in @@ -1324,6 +1346,8 @@ % " -> ' % 0xX -> hex2dec('X') % and collected in a struct. +% In addition, there seems to be "__attribute__" meta information for some +% items. All "field names" starting with "_" are currently silently ignored. ret=struct; % get ascconv data @@ -1332,25 +1356,30 @@ elseif isfield(hdr, 'CSAMiscProtocolHeaderInfoVB') X = get_numaris4_val(hdr.CSAMiscProtocolHeaderInfoVB,'MrPhoenixProtocol'); elseif isfield(hdr, 'CSASeriesHeaderInfo') - X=get_numaris4_val(hdr.CSASeriesHeaderInfo,'MrProtocol'); + X = get_numaris4_val(hdr.CSASeriesHeaderInfo,'MrProtocol'); else return; end -ascstart = strfind(X,'### ASCCONV BEGIN ###'); -ascend = strfind(X,'### ASCCONV END ###'); +X = regexprep(X,'^.*### ASCCONV BEGIN [^#]*###(.*)### ASCCONV END ###.*$','$1'); -if ~isempty(ascstart) && ~isempty(ascend) - tokens = textscan(char(X((ascstart+22):(ascend-1))),'%s', ... +if ~isempty(X) + tokens = textscan(char(X),'%s', ... 'delimiter',char(10)); - tokens{1}=regexprep(tokens{1},{'\[([0-9]*)\]','"(.*)"','^([^"]*)0x([0-9a-fA-F]*)','#.*'},{'($1+1)','''$1''','$1hex2dec(''$2'')',''}); + tokens{1}=regexprep(tokens{1},{'\[([0-9]*)\]','"(.*)"','^([^"]*)0x([0-9a-fA-F]*)','#.*','^.*\._.*$'},{'($1+1)','''$1''','$1hex2dec(''$2'')','',''}); % If everything would evaluate correctly, we could use % eval(sprintf('ret.%s;\n',tokens{1}{:})); for k = 1:numel(tokens{1}) - try - eval(['ret.' tokens{1}{k} ';']); - catch - disp(['AscConv: Error evaluating ''ret.' tokens{1}{k} ''';']); + if ~isempty(tokens{1}{k}) + try + [tlhrh, un] = regexp(tokens{1}{k}, '(?:=)+', 'split', 'match'); + [tlh, un] = regexp(tlhrh{1}, '(?:\.)+', 'split', 'match'); + tlh = cellfun(@genvarname, tlh, 'UniformOutput',false); + tlh = sprintf('.%s', tlh{:}); + eval(sprintf('ret%s = %s;', tlh, tlhrh{2})); + catch + disp(['AscConv: Error evaluating ''ret.' tokens{1}{k} ''';']); + end end end end diff --git a/spm_dicom_dict.mat b/spm_dicom_dict.mat index 5f6bd78c..fac46108 100644 Binary files a/spm_dicom_dict.mat and b/spm_dicom_dict.mat differ diff --git a/spm_dicom_dict.txt b/spm_dicom_dict.txt index 4bc2b072..77394cfb 100644 --- a/spm_dicom_dict.txt +++ b/spm_dicom_dict.txt @@ -3627,6 +3627,7 @@ FFFE E0DD SequenceDelimitationItem UN 1 0031 1009 NumberRawDataChannels UL 1 7fe1 1010 CSAData OB 1 0029 1140 SiemensMedcomHeader SQ 1 +0043 102f GE_ImageType OB 1 2001 0010 PrivateCreatorGroup2001 LO 1 2001 1001 ChemicalShift FL 1 2001 1002 ChemicalShiftNumberMR IS 1 diff --git a/spm_dicom_essentials.m b/spm_dicom_essentials.m index d483fbfa..30c79de2 100644 --- a/spm_dicom_essentials.m +++ b/spm_dicom_essentials.m @@ -11,7 +11,7 @@ % Copyright (C) 2008-2013 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_dicom_essentials.m 6361 2015-03-05 13:41:29Z john $ +% $Id: spm_dicom_essentials.m 6588 2015-11-03 15:26:38Z john $ used_fields = {... 'AcquisitionDate',... @@ -63,6 +63,7 @@ 'TransferSyntaxUID',... 'VROfPixelData',... 'ScanOptions',... + 'GE_ImageType',... ... 'PerFrameFunctionalGroupsSequence',... 'SharedFunctionalGroupsSequence',... diff --git a/spm_diffeo.mexw32 b/spm_diffeo.mexw32 index fde13403..fd07d72d 100755 Binary files a/spm_diffeo.mexw32 and b/spm_diffeo.mexw32 differ diff --git a/spm_diffeo.mexw64 b/spm_diffeo.mexw64 index e0aa8b66..54584ef1 100755 Binary files a/spm_diffeo.mexw64 and b/spm_diffeo.mexw64 differ diff --git a/spm_dilate_erode.mexw32 b/spm_dilate_erode.mexw32 index d723f279..3eb8d815 100755 Binary files a/spm_dilate_erode.mexw32 and b/spm_dilate_erode.mexw32 differ diff --git a/spm_dilate_erode.mexw64 b/spm_dilate_erode.mexw64 index f4800023..008f6ad9 100755 Binary files a/spm_dilate_erode.mexw64 and b/spm_dilate_erode.mexw64 differ diff --git a/spm_dot.m b/spm_dot.m new file mode 100644 index 00000000..8093a21f --- /dev/null +++ b/spm_dot.m @@ -0,0 +1,68 @@ +function [Y] = spm_dot(X,x,DIM) +% Multidimensional dot (inner) preoduct +% FORMAT [Y] = spm_dot(X,x,DIM) +% +% X - numeric array +% x - vector or cell array of numeric vectors +% +% Y - inner product obtained by summing the products of X and x along DIM +% +% If DIM is not specified the last dimension of X is used. If x is a cell +% array recursive dot products are computed (starting with the last entry +% if (the vector) DIM is not specified). +% +% See also: spm_cross +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_dot.m 6656 2015-12-24 16:49:52Z guillaume $ + + +% initialise X and vX +%-------------------------------------------------------------------------- +if iscell(x) + if nargin < 3 + DIM = (1:numel(x)) + numel(size(X)) - numel(x); + end + for i = 1:numel(x) + X = spm_dot(X,x{i},DIM(i)); + DIM = DIM - 1; + end + Y = X; + return +end + +% inner product +%========================================================================== +if nargin < 3, DIM = numel(size(X)); end + +% deal with simple cases +%-------------------------------------------------------------------------- +if isvector(X) + Y = X*x(:); + return +elseif ismatrix(X) + if DIM == 1 + Y = x'*X; + else + Y = X*x; + end + return +end + +d = size(X); +ind = cell(size(d)); +ind(:) = {':'}; +Y = X; + +for i = 1:numel(x) + sub = ind; + sub{DIM} = i; + Y(sub{:}) = X(sub{:}).*x(i); +end +Y = sum(Y,DIM); +if ~ismatrix(Y) + d(DIM) = []; + Y = reshape(Y,d); +end diff --git a/spm_eeg_assemble_priors.m b/spm_eeg_assemble_priors.m index e2cef976..a66bbbfd 100644 --- a/spm_eeg_assemble_priors.m +++ b/spm_eeg_assemble_priors.m @@ -1,28 +1,26 @@ -function [LCpL,Q,sumLCpL,QE,Cy,M,Cp,Cq,Lq] = spm_eeg_assemble_priors(L,Qp,Qe,ploton,h) -% Predict sensor level impact of sources in Qp given sensor noise Qe -% FORMAT [LCpL,Q,sumLCpL,QE,Cy,M,Cp,Cq,Lq] = spm_eeg_assemble_priors(L,Qp,Qe,ploton,h) -% L - lead fields -% Qp - priors on source level dipole moment nAm/(mm2) per sample -% Qe - sensor level variance in fT^2 per sample -% h - optional hyperparameters that scale the variance components in Qe -% and Qp (assume sensor followed by source level parameters) -% -% LCpL - sensor level covariance components corresponding to the source -% priors Qp -% Q - sparse array over sources holding dipole moment density nAm/(mm2) -% sumLCpL - predicted sensor level variance due to sources (in fT^2) -% QE - predicted sensor level noise variance (in fT^2) -% Cy - total sensor noise covariance predicted: Cy = QE+sumLCpL -% M - the MAP estimator : M = Cp*L'*inv(Qe + L*Cp*L')) -% Cp - the total source covariance matrix -% Cq - conditional source covariance- need to implement -% Lq - cell array of the L*q (impact of each source component at sensor level) -%__________________________________________________________________________ -% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging +function [LCpL,Q,sumLCpL,QE,Cy,M,Cp,Cq,Lq]=spm_eeg_assemble_priors(L,Qp,Qe,ploton,h) +%% function [LCpL,Q,sumLCpL,QE,Cy,M,Cp,Cq,Lq]=spm_eeg_assemble_priors(L,Qp,Qe,ploton,h) +%% to predict sensor level impact of sources in Qp given sensor noise Qe +%% L are the lead fields +%% Qp are priors on source level dipole moment nAm/(mm2) per sample +%% Qe is sensor level variance in fT^2 per sample +%% h are optional hyperparameters that scale the variance components in Qe and Qp (assume sensor followed by source level parameters) + +% LCpL are the sensor level covariance components corresponding to the +% source priors Qp +%% Q is a sparse array over sources holding dipole moment density nAm/(mm2) +%% sumLCpL is predicted sensor level variance due to sources (in fT^2) +%% QE is predicted sensor level noise variance (in fT^2) +%% Cy = QE+sumLCpL is the total sensor noise covariance predicted +%% M is the MAP estimator : M = Cp*L'*inv(Qe + L*Cp*L')) +%% Cp is the total source covariance matrix +%% Cq is conditional source covariance- need to implement +%% Lq is cell array of the L*q (impact of each source component at sensor level) + +% Copyright (C) 2010 Wellcome Trust Centre for Neuroimaging % Gareth Barnes -% $Id: spm_eeg_assemble_priors.m 6458 2015-05-27 16:22:09Z spm $ - +% $Id: spm_eeg_assemble_priors.m 6498 2015-07-15 19:13:31Z gareth $ if nargin<4, ploton=[]; @@ -74,10 +72,15 @@ Q(:,i)=q*v; - % Lq is the sensor level projection of the prior Q{i}.q + %% Lq is the sensor level projection of the prior Q{i}.q Lq{i}.q = L*Q(:,i); %% supply an eigen mode in q - + else %% no .q field, check it is 2D + if size(Qp{i},1)~=size(Qp{i},2), + disp('making Qp 2D'); + Qp{i}=diag(sparse(Qp{i})); + end; end; + end; % if iscell end @@ -104,7 +107,8 @@ LQp = L*Q(:,i); LCpL{i}=LQp*LQp'; Cp = Cp + Q(:,i)*Q(:,i)'; %% Q is already scaled by root hp - LCp = LCp+LQp; + %LCp = LCp+LQp; + LCp = LCp+L*Cp; else % full matrix version Qtmp=Qp{i}*hp(i); LCpL{i}=L*Qtmp*L'; @@ -138,15 +142,12 @@ % Cq = Cp - Cp*L'*iC*L*Cp; %---------------------------------------------------------------------- %Cq = Cp - sum(LCp.*M')'; in original -%Cq = diag(Cp) - sum(LCp.*M')'; -%keyboard -%Cq = diag(Cp) - sum(LCp.*M')'; -Cq=[]; +Cq = diag(Cp) - sum(LCp.*M')'; disp('end assemble'); -if ploton +if ploton, subplot(3,1,1); imagesc(sumLCpL);colorbar; title('source prior'); @@ -156,4 +157,6 @@ subplot(3,1,3); imagesc(Cy);colorbar; title('Sensor covariance per sample (fT^2)'); -end +end; + + diff --git a/spm_eeg_contrast.m b/spm_eeg_contrast.m index c1dc8ea4..a0350f06 100644 --- a/spm_eeg_contrast.m +++ b/spm_eeg_contrast.m @@ -28,9 +28,9 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel, Rik Henson -% $Id: spm_eeg_contrast.m 5560 2013-06-18 10:49:00Z vladimir $ +% $Id: spm_eeg_contrast.m 6526 2015-08-20 10:28:36Z vladimir $ -SVNrev = '$Rev: 5560 $'; +SVNrev = '$Rev: 6526 $'; %-Startup %-------------------------------------------------------------------------- @@ -129,6 +129,7 @@ %-------------------------------------------------------------------------- Dnew = conditions(Dnew, ':', S.label); Dnew = trialonset(Dnew, ':', []); +Dnew = trialtag(Dnew, ':', []); Dnew = badtrials(Dnew, ':', 0); Dnew = repl(Dnew, ':', newrepl); Dnew = type(Dnew, 'evoked'); diff --git a/spm_eeg_convert2images.m b/spm_eeg_convert2images.m index 6c016d19..da1daed1 100644 --- a/spm_eeg_convert2images.m +++ b/spm_eeg_convert2images.m @@ -1,4 +1,4 @@ -function images = spm_eeg_convert2images(S) +function [images, outroot] = spm_eeg_convert2images(S) % Convert M/EEG data to images for statistical analysis % FORMAT images = spm_eeg_convert2images(S) % @@ -11,6 +11,7 @@ % 'scalp x time' % 'scalp x frequency' (average over time) % 'scalp' (average over time and frequency) +% 'source' (average over time and frequency) % 'time x frequency' (average over channels) % 'time' (1D average over channels, frequency) % 'frequency' (1D average over channels, time) @@ -28,12 +29,12 @@ % output: % images - list of generated image files or objects %__________________________________________________________________________ -% Copyright (C) 2005-2012 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2005-2015 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak, James Kilner, Stefan Kiebel -% $Id: spm_eeg_convert2images.m 6369 2015-03-10 10:46:49Z vladimir $ +% $Id: spm_eeg_convert2images.m 6653 2015-12-22 09:32:06Z vladimir $ -SVNrev = '$Rev: 6369 $'; +SVNrev = '$Rev: 6653 $'; %-Startup %-------------------------------------------------------------------------- @@ -67,15 +68,25 @@ error('Selected frequency window is invalid.'); end + freqonset = D.frequencies(freqind(1)); df = unique(diff(D.frequencies(freqind))); - if length(df)> 1 && (max(diff(df))/mean(df))>0.1 - error('Irregular frequency spacing'); + if length(df)> 1 + if (max(diff(df))/mean(df))>0.1 + df = unique(diff(log(D.frequencies(freqind)))); + if (max(diff(df))/mean(df))>0.1 + error('Irregular frequency spacing'); + else + freqonset = log(D.frequencies(freqind(1))); + end + end end + df = mean(df); else df = 0; end isscalp = strncmpi('scalp', S.mode, 5); +ismesh = false; chanind = setdiff(D.selectchannels(S.channels), D.badchannels); @@ -124,7 +135,7 @@ N.mat = [... V(1) 0 0 -C(1);... 0 V(2) 0 -C(2);... - 0 0 df D.frequencies(freqind(1));... + 0 0 df freqonset;... 0 0 0 1]; N.mat(3,4) = N.mat(3,4) - N.mat(3,3); @@ -142,6 +153,20 @@ dat = file_array('', [n n], 'FLOAT32-LE'); + case 'source' + avflag = [0 1 1]; + + g = D.sensors('SRC'); + + if ~isempty(g) + g = gifti(g); + ismesh = true; + end + + if isempty(g) || size(g.vertices, 1) ~= length(chanind) + error('The number of source channels should match the number of mesh vertices.'); + end + case 'time x frequency' if ~isTF error('This mode is only supported for TF datasets.'); @@ -150,7 +175,7 @@ avflag = [1 0 0]; N.mat = [... - df 0 0 D.frequencies(freqind(1));... + df 0 0 freqonset;... 0 1e3/D.fsample 0 time(D, timeind(1), 'ms');... 0 0 1 0;... 0 0 0 1]; @@ -175,7 +200,7 @@ avflag = [1 0 1]; N.mat(1, 1) = df; - N.mat(1, 4) = D.frequencies(freqind(1)) - N.mat(1, 1); + N.mat(1, 4) = freqonset - N.mat(1, 1); dat = file_array('', [length(freqind) 1], 'FLOAT32-LE'); @@ -185,7 +210,7 @@ N.mat(1, 4) = time(D, timeind(1), 'ms'); if isTF - N.mat(2, 4) = D.frequencies(freqind(1)); + N.mat(2, 4) = freqonset; end dat = file_array('', [1 1], 'FLOAT32-LE'); @@ -232,23 +257,30 @@ condlabel = sprintf('condition%04d', c); end - fname = fullfile(outroot, ['condition_' condlabel '.nii']); - - cdat = dat; - cdat.fname = fname; - cdat.dim = [dat.dim ones(1, 3-length(dat.dim)) length(trialind)]; - N.dat = cdat; - create(N); + if ismesh + res = mkdir(outroot, ['condition_' condlabel]); + + save(g, fullfile(outroot, ['condition_' condlabel], [spm_file(D.fname, 'basename'), '.surf.gii'])); + + G = gifti; + G.private.metadata(1).name = 'SurfaceID'; + G.private.metadata(1).value = fullfile(outroot, ['condition_' condlabel], [spm_file(D.fname, 'basename'), '.surf.gii']); + + else + fname = fullfile(outroot, ['condition_' condlabel '.nii']); + + cdat = dat; + cdat.fname = fname; + cdat.dim = [dat.dim ones(1, 3-length(dat.dim)) length(trialind)]; + N.dat = cdat; + create(N); + end spm_progress_bar('Init', length(trialind),['Converting condition ',S.conditions{c}],'Trial'); if length(trialind) > 100, Ibar = floor(linspace(1, length(trialind), 100)); else Ibar = 1:length(trialind); end for i = 1:length(trialind) - images{ind} = [fname ',' num2str(i)]; - - ind = ind + 1; - Y = subsref(D, struct('type', '()', 'subs', {[dataind, {trialind(i)}]})); for m = sort(find(avflag), 1, 'descend') @@ -265,23 +297,38 @@ switch length(N.dat.dim) case 2 - N.dat(:,:) = YY; + N.dat(:,:) = YY; case 3 - N.dat(:,:, j) = YY; + N.dat(:,:, j) = YY; case 4 - N.dat(:,:,j,i) = YY; + N.dat(:,:,j,i) = YY; otherwise error('Invalid output file'); end end + + images{ind} = [fname ',' num2str(i)]; + + elseif ismesh + fname = fullfile(outroot, ['condition_' condlabel], ['condition_' condlabel '_' sprintf('_%04d',trialind(i)) '.gii']); + + G.cdata = Y(:); + + save(G, fname, 'ExternalFileBinary'); + + images{ind} = fname; else if size(Y, 1) == 1 Y = Y'; end N.dat(:, :, 1, i) = Y; + + images{ind} = [fname ',' num2str(i)]; end + ind = ind + 1; + if ismember(i, Ibar), spm_progress_bar('Set', i); end end end diff --git a/spm_eeg_downsample.m b/spm_eeg_downsample.m index 59ad6ade..3055c3ce 100644 --- a/spm_eeg_downsample.m +++ b/spm_eeg_downsample.m @@ -5,12 +5,14 @@ % S - optional input struct % (optional) fields of S: % S.D - MEEG object or filename of M/EEG mat-file +% S.method - resampling method. Can be 'resample' (default), 'decimate', +% 'downsample', 'fft' % S.fsample_new - new sampling rate, must be lower than the original one % S.prefix - prefix for the output file (default - 'd') % % D - MEEG object (also written on disk) %__________________________________________________________________________ -% +% % This function uses the Signal Processing toolbox from The MathWorks: % http://www.mathworks.com/products/signal/ % (function resample.m) if present and spm_timeseries_resample.m otherwise. @@ -18,46 +20,47 @@ % Copyright (C) 2005-2014 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_downsample.m 6016 2014-05-23 17:34:06Z guillaume $ +% $Id: spm_eeg_downsample.m 6614 2015-11-30 10:42:02Z vladimir $ -SVNrev = '$Rev: 6016 $'; +SVNrev = '$Rev: 6614 $'; %-Startup %-------------------------------------------------------------------------- spm('FnBanner', mfilename, SVNrev); spm('FigName','M/EEG downsampling'); spm('Pointer','Watch'); -%-Test for the presence of Signal Processing Matlab toolbox -%-------------------------------------------------------------------------- +if ~isfield(S, 'prefix'), S.prefix = 'd'; end +if ~isfield(S, 'method'), S.method = 'resample'; end + flag_tbx = license('checkout','signal_toolbox') && ~isempty(ver('signal')); -if ~flag_tbx - disp(['warning: using homemade resampling routine ' ... - 'as signal toolbox is not available.']); +if ~flag_tbx && ~isequal(S.method, 'fft') + S.method = 'fft'; + disp(['warning: switching to ''fft'' method ' ... + 'as signal toolbox is not available.']); end -if ~isfield(S, 'prefix'), S.prefix = 'd'; end - %-Get MEEG object %-------------------------------------------------------------------------- D = spm_eeg_load(S.D); % This is to handle non-integer sampling rates up to a reasonable precision -P = round(10*S.fsample_new); -Q = round(10*D.fsample); +P = round(10*S.fsample_new)/10; +Q = round(10*D.fsample)/10; %-First pass: Determine new D.nsamples %========================================================================== -if flag_tbx % Signal Proc. Toolbox - nsamples_new = ceil(nsamples(D)*P/Q); +t = ft_preproc_resample(D.time, Q, P, S.method); +nsamples_new = size(t, 2); +fsample_new = (nsamples_new/D.nsamples)*D.fsample; + +if abs(S.fsample_new - fsample_new)<=0.1 + fsample_new = S.fsample_new; else - d = double(squeeze(D(1, :, 1))); - [d2,alpha] = spm_timeseries_resample(d,P/Q); - fsample_new = D.fsample*alpha; - S.fsample_new = fsample_new; - disp(['Resampling frequency is ',num2str(fsample_new), 'Hz']) - nsamples_new = size(d2, 2); + fsample_new = round(10*fsample_new)/10; end +disp(['Resampling frequency is ',num2str(fsample_new), 'Hz']) + %-Generate new meeg object with new filenames %-------------------------------------------------------------------------- Dnew = clone(D, [S.prefix fname(D)], [D.nchannels nsamples_new D.ntrials]); @@ -87,25 +90,19 @@ chncnt=chncnt+blksz; spm_progress_bar('Set','ylabel','writing...'); - if flag_tbx % Signal Proc. Toolbox - Dnew(blkchan,:) = resample(Dtemp', P, Q)'; - else - Dnew(blkchan,:) = spm_timeseries_resample(Dtemp,P/Q); - end + + Dnew(blkchan,:) = ft_preproc_resample(Dtemp, Q, P, S.method); + spm_progress_bar('Set', blkchan(end)); - end + end else %-Epoched %---------------------------------------------------------------------- spm_progress_bar('Init', D.ntrials, 'Trials downsampled'); drawnow; if D.ntrials > 100, Ibar = floor(linspace(1, D.ntrials, 100)); else Ibar = 1:D.ntrials; end - for i = 1:D.ntrials - if flag_tbx % signal proc. toolbox - Dnew(:, :, i) = resample(spm_squeeze(D(:, :, i), 3)', P, Q)'; - else - Dnew(:, :, i) = spm_timeseries_resample(spm_squeeze(D(:, :, i), 3),P/Q); - end + for i = 1:D.ntrials + Dnew(:, :, i) = ft_preproc_resample(spm_squeeze(D(:, :, i), 3), Q, P, S.method); if ismember(i, Ibar), spm_progress_bar('Set', i); end diff --git a/spm_eeg_epochs.m b/spm_eeg_epochs.m index 7b479e40..58da7445 100644 --- a/spm_eeg_epochs.m +++ b/spm_eeg_epochs.m @@ -49,9 +49,9 @@ % Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_epochs.m 6407 2015-04-15 14:36:14Z guillaume $ +% $Id: spm_eeg_epochs.m 6596 2015-11-10 15:52:16Z vladimir $ -SVNrev = '$Rev: 6407 $'; +SVNrev = '$Rev: 6596 $'; %-Startup %-------------------------------------------------------------------------- @@ -220,7 +220,7 @@ end Dnew = events(Dnew, i, select_events(D.events, ... - [trl(i, 1)/D.fsample-S.eventpadding trl(i, 2)/D.fsample+S.eventpadding])); + D.trialonset+[trl(i, 1)/D.fsample-S.eventpadding trl(i, 2)/D.fsample+S.eventpadding])); if ismember(i, Ibar), spm_progress_bar('Set', i); end end diff --git a/spm_eeg_fixpnt.m b/spm_eeg_fixpnt.m new file mode 100644 index 00000000..8809b6e0 --- /dev/null +++ b/spm_eeg_fixpnt.m @@ -0,0 +1,44 @@ +function data = spm_eeg_fixpnt(data, recurse) +% Helper function to replace pos by pnt +%__________________________________________________________________________ +% Copyright (C) 2016 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_eeg_fixpnt.m 6683 2016-01-15 16:15:32Z guillaume $ + +if nargin==1 + recurse = 1; +end + +if ~isa(data, 'struct') + return; +end + +if numel(data)>1 + % loop over all individual elements + clear tmp + for i=1:numel(data) + % this is to prevent an "Subscripted assignment between dissimilar structures" error + tmp(i) = spm_eeg_fixpnt(data(i)); + end + data = tmp; + clear tmp + return +end + +% replace pos by pnt +if isfield(data, 'pos') + data.pnt = data.pos; + data = rmfield(data, 'pos'); +end + +if recurse<3 + % recurse into substructures, not too deep + fn = fieldnames(data); + fn = setdiff(fn, {'cfg'}); % don't recurse into the cfg structure + for i=1:length(fn) + if isstruct(data.(fn{i})) + data.(fn{i}) = spm_eeg_fixpnt(data.(fn{i}), recurse+1); + end + end +end diff --git a/spm_eeg_grandmean.m b/spm_eeg_grandmean.m index 5f056111..6c5e218d 100644 --- a/spm_eeg_grandmean.m +++ b/spm_eeg_grandmean.m @@ -26,9 +26,9 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Stefan Kiebel -% $Id: spm_eeg_grandmean.m 6325 2015-02-03 10:54:03Z vladimir $ +% $Id: spm_eeg_grandmean.m 6625 2015-12-03 21:49:24Z vladimir $ -SVNrev = '$Rev: 6325 $'; +SVNrev = '$Rev: 6625 $'; %-Startup %-------------------------------------------------------------------------- @@ -214,6 +214,7 @@ % The order of the conditions will be consistent with the first file [sel1, sel2] = spm_match_str(D{1}.condlist, types); +sel2 = sel2(:)'; types = types([sel2, setdiff(1:length(types), sel2)]); Ntypes = numel(types); @@ -349,6 +350,7 @@ Do = repl(Do, ':', nrepl); Do = badtrials(Do, ':', 0); Do = trialonset(Do, ':', []); +Do = trialtag(Do, ':', []); Do = Do.history('spm_eeg_grandmean', S, 'reset'); save(Do); diff --git a/spm_eeg_inv_custom_ui.m b/spm_eeg_inv_custom_ui.m index 4421407b..c8ad4689 100644 --- a/spm_eeg_inv_custom_ui.m +++ b/spm_eeg_inv_custom_ui.m @@ -21,7 +21,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_eeg_inv_custom_ui.m 5568 2013-07-01 11:07:18Z vladimir $ +% $Id: spm_eeg_inv_custom_ui.m 6633 2015-12-04 17:09:24Z vladimir $ % defaults from D is specified %========================================================================== @@ -97,15 +97,21 @@ % Source space restictions %---------------------------------------------------------------------- - if spm_input('Restrict solutions','+1','no|yes',[0 1],1) - [P, sts] = spm_select(1, 'mat', 'Select source (n x 3) location file'); - if sts - xyz = load(P); - name = fieldnames(xyz); - inverse.xyz = xyz.(name{1}); - inverse.rad = spm_input('radius of VOI (mm)','+1','r',32); - end - + switch spm_input('Restrict solutions','+1','no|roi|mask',[0, 1, 2],1) + case 1 + [P, sts] = spm_select(1, 'mat', 'Select source (n x 3) location file'); + if sts + xyz = load(P); + name = fieldnames(xyz); + inverse.xyz = xyz.(name{1}); + inverse.rad = spm_input('radius of VOI (mm)','+1','r',32); + end + case 2 + f = '(.*\.nii(,\d+)?$)|(.*\.img(,\d+)?$)'; + [P, sts] = spm_select(1, f, 'Select mask image'); + if sts + inverse.mask = P; + end end end diff --git a/spm_eeg_inv_forward.m b/spm_eeg_inv_forward.m index 97365ab3..ece0bb28 100644 --- a/spm_eeg_inv_forward.m +++ b/spm_eeg_inv_forward.m @@ -12,7 +12,7 @@ % Copyright (C) 2008-2014 Wellcome Trust Centre for Neuroimaging % Jeremie Mattout & Christophe Phillips -% $Id: spm_eeg_inv_forward.m 6182 2014-09-18 12:03:18Z guillaume $ +% $Id: spm_eeg_inv_forward.m 6590 2015-11-05 16:17:27Z vladimir $ %-Initialisation @@ -122,12 +122,12 @@ end cfg = []; - cfg.vol = vol; + cfg.headmodel = vol; cfg.grid.pos = mesh.tess_ctx.vert; cfg.moveinward = 6e-3; %move to empirically determined BEM safe zone gridcorrect = ft_prepare_sourcemodel(cfg); - mesh_correction = rmfield(cfg, {'vol', 'grid'}); + mesh_correction = rmfield(cfg, {'headmodel', 'grid'}); mesh.tess_ctx.vert = gridcorrect.pos; diff --git a/spm_eeg_inv_mesh.m b/spm_eeg_inv_mesh.m index e133d464..e9372d17 100644 --- a/spm_eeg_inv_mesh.m +++ b/spm_eeg_inv_mesh.m @@ -13,7 +13,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Jeremie Mattout & Christophe Phillips -% $Id: spm_eeg_inv_mesh.m 3833 2010-04-22 14:49:48Z vladimir $ +% $Id: spm_eeg_inv_mesh.m 6682 2016-01-15 15:52:00Z vladimir $ % SPM directory of canonical anatomy @@ -108,7 +108,7 @@ % datareg %-------------------------------------------------------------------------- -fid = ft_read_headshape(fullfile(spm('dir'), 'EEGtemplates', 'fiducials.sfp')); +fid = spm_eeg_fixpnt(ft_read_headshape(fullfile(spm('dir'), 'EEGtemplates', 'fiducials.sfp'))); mesh.fid = export(gifti(mesh.tess_scalp), 'ft'); mesh.fid.unit = 'mm'; diff --git a/spm_eeg_inv_vbecd_disp.m b/spm_eeg_inv_vbecd_disp.m index 30d95663..a8648c94 100644 --- a/spm_eeg_inv_vbecd_disp.m +++ b/spm_eeg_inv_vbecd_disp.m @@ -9,10 +9,10 @@ function spm_eeg_inv_vbecd_disp(action,varargin) % Display the ind^th .inv{} cell element, if it is actually a VB-ECD % solution. %__________________________________________________________________________ -% Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging % Christophe Phillips -% $Id: spm_eeg_inv_vbecd_disp.m 5776 2013-12-04 15:20:18Z gareth $ +% $Id: spm_eeg_inv_vbecd_disp.m 6501 2015-07-17 14:32:09Z spm $ % Note: % unfortunately I cannot see how to ensure that when zooming in the image @@ -234,7 +234,7 @@ function spm_eeg_inv_vbecd_disp(action,varargin) % Place the underlying image at right cuts -%% GRB FIX- removing the line below July 2013 +% GRB FIX- removing the line below July 2013 %spm_orthviews('Reposition',loc_mm); @@ -321,7 +321,7 @@ function spm_eeg_inv_vbecd_disp(action,varargin) set(sdip.hdl.hor1,'String',[num2str(Njs_m(1)),' ',num2str(Njs_m(2)), ... ' ',num2str(Njs_m(3))]); -set(sdip.hdl.hor2,'String',[num2str(Angle(1)),'� ',num2str(Angle(2)),'�']); +set(sdip.hdl.hor2,'String',[num2str(Angle(1)),' ',num2str(Angle(2)),' ']); set(sdip.hdl.int,'String',Ijs_m); % Change the colour of toggle button of dipoles actually displayed diff --git a/spm_eeg_invert.m b/spm_eeg_invert.m index 99c43820..e9c83a44 100644 --- a/spm_eeg_invert.m +++ b/spm_eeg_invert.m @@ -122,7 +122,7 @@ % Copyright (C) 2006-2014 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_eeg_invert.m 6182 2014-09-18 12:03:18Z guillaume $ +% $Id: spm_eeg_invert.m 6636 2015-12-05 23:28:50Z vladimir $ % check whether this is a group inversion for (Nl) number of subjects %-------------------------------------------------------------------------- @@ -154,6 +154,7 @@ try, Nr = inverse.Nr; catch, Nr = 16; end try, xyz = inverse.xyz; catch, xyz = [0 0 0]; end try, rad = inverse.rad; catch, rad = 128; end +try, mask = inverse.mask; catch, mask = []; end try, lpf = inverse.lpf; catch, lpf = 0; end try, hpf = inverse.hpf; catch, hpf = 48; end try, sdv = inverse.sdv; catch, sdv = 4; end @@ -358,14 +359,30 @@ % Restrict source space to Ns sources by eliminating dipoles %-------------------------------------------------------------------------- -Is = sparse(Nd,1); -for i = 1:Nv - Iv = sum([vert(:,1) - xyz(i,1), ... - vert(:,2) - xyz(i,2), ... - vert(:,3) - xyz(i,3)].^2,2) < rad(i)^2; - Is = Is | Iv; +if any(any(xyz)) || ~isempty(mask) + + Is = sparse(Nd,1); + + if any(any(xyz)) + for i = 1:Nv + Iv = sum([vert(:,1) - xyz(i,1), ... + vert(:,2) - xyz(i,2), ... + vert(:,3) - xyz(i,3)].^2,2) < rad(i)^2; + Is = Is | Iv; + end + end + + if ~isempty(mask) + Iv = spm_mesh_project(struct('vertices',vert,'faces',face), mask); + Is = Is | Iv(:); + end + + Is = find(Is); +else + Is = 1:Nd; end -Is = find(Is); + + vert = vert(Is,:); QG = QG(Is,Is); for m = 1:Nmod diff --git a/spm_eeg_invert_EBoptimise.m b/spm_eeg_invert_EBoptimise.m index 159c0ce0..f379208c 100644 --- a/spm_eeg_invert_EBoptimise.m +++ b/spm_eeg_invert_EBoptimise.m @@ -1,27 +1,29 @@ function [F,M,Cq,Cp,QE,Qp] = spm_eeg_invert_EBoptimise(AY,UL,opttype,Qp,Qe,Qe0) -% Empirical Bayes optimization of priors Qp and Qe to fit data AY based on lead fields UL -% FORMAT [F,M,Cq,Cp,QE,Qp] = spm_eeg_invert_EBoptimise(AY,UL,opttype,Qp,Qe,Qe0) -% AY - concatenated dimension reduced trials of M/EEG data -% UL - dimension reduced lead field -% opttype - how to optimize 'ARD','GS' or 'REML' -% Qp - source level priors: where Qp{i}.q holds an eigenmode. -% So source covariance component is Qp{i}.q*Qp{i}.q'. -% Alternately Qp{i} could be full source covariance component -% Qe - sensor noise prior -% Qe0 - floor of noise power to signal power (posterior estimate of -% sensor noise will always be at least this big) -% -% F - free energy -% M - MAP operator -% Cq - conditional variance -% Cp - source level posterior (source by source variance) -% QE - sensor noise posterior -% Qp - contains the posterior in same form as prior -%__________________________________________________________________________ -% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging +%% function [F,M,Cq,Cp,QE,Qp] = spm_eeg_invert_EBoptimise(AY,UL,opttype,Qp,Qe,Qe0) +%% Empirical Bayes optimization of priors Qp and Qe to fit data AY based on lead fields UL +% AY concatenated dimension reduced trials of M/EEG data +% UL dimension reduced lead field +% Qp source level priors- where Qp{i}.q holds an eigenmode. So source covariance +% component is Qp{i}.q*Qp{i}.q'. +% Alternately Qp{i} could be full source covariance component +% Qe sensor noise prior +% Qe0 floor of noise power to signal power (posteiror estimate of sensor noise will always be at +% least this big) +% opttype- how to optimize 'ARD','GS' or 'REML' + + +%% QE sensor noise posterior +%% Cp source level posterior (source by source variance) +%% F free energy +%% M MAP operator +%% Cq conditional variance +%% F free energy +%% Qp contains the posterior in same form as prior + +% Copyright (C) 2010 Wellcome Trust Centre for Neuroimaging % Gareth Barnes -% $Id: spm_eeg_invert_EBoptimise.m 6458 2015-05-27 16:22:09Z spm $ +% $Id: spm_eeg_invert_EBoptimise.m 6494 2015-07-06 10:23:04Z gareth $ if ~iscell(Qe), @@ -55,7 +57,7 @@ if isfield(opttype{j},'GSopt'), % Greedy search over MSPs - % needs to work with sparse covariance matrices Qp{i}.q + %% needs to work with sparse covariance matrices Qp{i}.q %------------------------------------------------------------------ if isempty(AY), @@ -68,7 +70,7 @@ [LQpL,Q,sumLQpL,QE,Cy,M,Cp,Cq,Lq]=spm_eeg_assemble_priors(UL,Qp,Qe,ploton); MVB = spm_mvb(AY,UL,[],Q,Qe,16); - % THE VERSION BELOW IS MORE PEDESTRIAN BUT EASIER TO FOLLOW + %% THE VERSION BELOW IS MORE PEDESTRIAN BUT EASIER TO FOLLOW % MVB_grb = spm_mvb_slow_grb( AY,UL,Q,QE,16,Q0 ); Ne=length(Qe); @@ -88,7 +90,7 @@ F=max(MVB.F); else - disp('Skipping ARD as no eigenmode priors'); + disp('Skipping GS as no eigenmode priors'); F=-Inf; end; @@ -97,12 +99,12 @@ end; %%GS if isfield(opttype{j},'ARDopt'), - % needs to work with sparse (svd decomposed) source covariance matrices + %% needs to work with sparse (svd decomposed) source covariance matrices Nn=size(AY,2); %% number of data samples used to make up covariance matrix - [LQpL,Q,sumLQpL,QE,Cy,M,Cp,Cq,Lq]=spm_eeg_assemble_priors(UL,Qp,Qe,ploton); +% [LQpL,Q,sumLQpL,QE,Cy,M,Cp,Cq,Lq]=spm_eeg_assemble_priors(UL,Qp,Qe,ploton); if ~isempty(sparseind), - Qp=Qp_sp; + [LQpL,Q,sumLQpL,QE,Cy,M,Cp,Cq,Lq]=spm_eeg_assemble_priors(UL,Qp,Qe,ploton); @@ -111,10 +113,17 @@ %------------------------------------------------------------------ - % SPM_SP_REML STARTS WITH EIGEN MODES Lq RATHER THAN FULL COV MATRICES - [Cy,h,Ph,F0] = spm_sp_reml(AYYA,[],[Qe Lq],Nn); + %% SPM_SP_REML STARTS WITH EIGEN MODES Lq RATHER THAN FULL COV MATRICES + fprintf('entering REML for ARD') + %[Cy,h,Ph,F0] = spm_sp_reml(AYYA,[],[Qe Lq],Nn); + [Cy,h,Ph,F0] = spm_sp_reml(AYYA,[],[Qe Lq],Nn); + + if isnan(F0), + warning('NAN in ARD stage, dropping out'); + return; + end; % Spatial priors (QP) %------------------------------------------------------------------ % h provides the final weights of the hyperparameters @@ -131,6 +140,7 @@ h=[h(1:Ne) hp(keepind)']; [LQpL,Q,sumLQpL,QE,Csensor,M,Cp,Cq,Lq]=spm_eeg_assemble_priors(UL,Qp(keepind),Qe,ploton,h); + [Cy,h2,Ph,F] = spm_sp_reml(AYYA,[],[Qe Lq],Nn); [LQpL,Q,sumLQpL,QE,Csensor,M,Cp,Cq,Lq]=spm_eeg_assemble_priors(UL,Qp(keepind),Qe,ploton,h2); % Accumulate empirical priors (New set of patches for the second inversion) @@ -176,18 +186,18 @@ - % NOW OPTMIZE MIXTURE OF PRIOR COVARIANCE COMPS WITH REML + %%% NOW OPTMIZE MIXTURE OF PRIOR COVARIANCE COMPS WITH REML [Cy,h,Ph,F] = spm_reml_sc(AYYA,[],[Qe LQpL],Nn,-4,16,Q0); - % Now convert the original priors to scaled versions of themselves + %% Now convert the original priors to scaled versions of themselves [LCpL,Q,sumLCpL,QE,Cy,M,Cp,Cq]=spm_eeg_assemble_priors(UL,Qp,Qe,ploton,h); - % THIS NEXT LINE IS JUST A CHECK THAT THE POSTERIORS WORK AS PRIORS F2 should be greater or equal to F + %% THIS NEXT LINE IS JUST A CHECK THAT THE POSTERIORS WORK AS PRIORS F2 should be greater or equal to F [Cy2,h2,Ph2,F2] = spm_reml_sc(AYYA,[],[{QE} {sumLCpL}],Nn,-4,16,Q0); @@ -217,6 +227,9 @@ + + + function [Qp,Q]=compact_form(QP) diff --git a/spm_eeg_invert_classic.m b/spm_eeg_invert_classic.m index fa7f8b19..8df6a4e1 100644 --- a/spm_eeg_invert_classic.m +++ b/spm_eeg_invert_classic.m @@ -62,33 +62,30 @@ % inverse.scale - scaling of data for each of j modalities %__________________________________________________________________________ % -% Created by: Jose David Lopez - ralph82co@gmail.com -% Gareth Barnes - g.barnes@fil.ion.ucl.ac.uk -% Vladimir Litvak - litvak.vladimir@gmail.com -% -% % This version is for single subject single modality analysis and therefore % contains none of the associated scaling factors. % No symmetric priors are used in this implementation (just single patches) % There is an option for a Beamforming prior : inversion type 'EBB' -% also added new beamforming method- using GS rather than ARD- from Juan David Martinez Vargas 'EBBgs' - -%%The code was used in -%% L�pez, J. D., Penny, W. D., Espinosa, J. J., Barnes, G. R. (2012). -% A general Bayesian treatment for MEG source reconstruction incorporating lead field uncertainty. +% also added new beamforming method- using GS rather than ARD- from Juan +% David Martinez Vargas 'EBBgs' +% +% The code was used in +% Lopez, J. D., Penny, W. D., Espinosa, J. J., Barnes, G. R. (2012). +% A general Bayesian treatment for MEG source reconstruction incorporating +% lead field uncertainty. % Neuroimage 60(2), 1194-1204 doi:10.1016/j.neuroimage.2012.01.077. - -% $Id: spm_eeg_invert_classic.m 6382 2015-03-19 11:26:20Z holly $ - +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Jose David Lopez, Gareth Barnes, Vladimir Litvak +% $Id: spm_eeg_invert_classic.m 6501 2015-07-17 14:32:09Z spm $ Nl = length(D); - - -if Nl>1, +if Nl>1 error('function only defined for a single subject'); -end; +end % D - SPM data structure %========================================================================== @@ -307,7 +304,7 @@ It = fix(It); disp(sprintf('Number of samples %d',length(It))); - + % Peristimulus time %---------------------------------------------------------------------- @@ -332,7 +329,7 @@ T = T(:,j); % Apply the filter to discrete cosines dct = dct(j); % Frequencies accepted -%% Hanning window +% Hanning window %---------------------------------------------------------------------- if Han @@ -383,7 +380,7 @@ %====================================================================== -if isempty(Nt), %% automatically assign appropriate number of temporal modes +if isempty(Nt), %% automatically assign appropriate number of temporal modes [U E] = spm_svd(YTY,exp(-8)); % get temporal modes if isempty(U), %% fallback warning('nothing found using spm svd, using svd'); @@ -413,7 +410,7 @@ % get spatial covariance (Y*Y') for Gaussian process model %====================================================================== -% loop over Ntrialtypes trial types +% loop over Ntrialtypes trial types %---------------------------------------------------------------------- UYYU = 0; AYYA=0; @@ -428,7 +425,7 @@ [c1,ib]=intersect(c,badtrialind); %% remove bad trials ib if there are any c=c(setxor(1:length(c),ib)); Nk = length(c); - + % loop over epochs %------------------------------------------------------------------ for k = 1:Nk @@ -438,7 +435,7 @@ Y = D(Ic,It,c(k))*S; %% in temporal subspace Y=A*Y; %% in spatial subspace - + % accumulate first & second-order responses %-------------------------------------------------------------- @@ -493,28 +490,12 @@ %-------------------------------------------------------------- q = QG(:,Ip(i)); Qp{end + 1}.q = q; - LQpL{end + 1}.q = UL*q; + LQpL{end + 1}.q = UL*q; end -% case {'EBB'} -% % create beamforming prior. See: -% % Source reconstruction accuracy of MEG and EEG Bayesian inversion approaches. -% %Belardinelli P, Ortiz E, Barnes G, Noppeney U, Preissl H. PLoS One. 2012;7(12):e51985. -% %------------------------------------------------------------------ -% InvCov = spm_inv(YY); -% allsource = zeros(Ns,1); -% Sourcepower = zeros(Ns,1); -% for bk = 1:Ns -% normpower = 1/(UL(:,bk)'*UL(:,bk)); -% Sourcepower(bk) = 1/(UL(:,bk)'*InvCov*UL(:,bk)); -% allsource(bk) = Sourcepower(bk)./normpower; -% end -% allsource = allsource/max(allsource); % Normalise -% -% Qp{1} = diag(allsource); -% LQpL{1} = UL*diag(allsource)*UL'; - case {'EBB'} - % create SMOOTH beamforming prior. + + case {'EBB'} + % create SMOOTH beamforming prior. disp('NB smooth EBB algorithm !'); %------------------------------------------------------------------ InvCov = spm_inv(AYYA); @@ -533,22 +514,56 @@ Qp{1} = diag(allsource); LQpL{1} = UL*diag(allsource)*UL'; - case {'EBBgs'} % NEW BEAMFORMER PRIOR!! - % create beamforming prior- Juan David- Martinez Vargas - %------------------------------------------------------------------ - allsource = zeros(Ntrials,Ns); - for ii = 1:Ntrials - InvCov = spm_inv(YYep{ii}); - Sourcepower = zeros(Ns,1); - for bk = 1:Ns - normpower = 1/(UL(:,bk)'*UL(:,bk)); - Sourcepower(bk) = 1/(UL(:,bk)'*InvCov*UL(:,bk)); - allsource(ii,bk) = Sourcepower(bk)./normpower; - end + case {'EBBsparse'} + + %% calculate power on cortical surface + %% using beamformer assumptions + disp('NB sparse and smooth EBB algorithm !'); + + InvCov = spm_inv(AYYA); + allsource = sparse(Ns,1); + Sourcepower = sparse(Ns,1); + for bk = 1:Ns + q = QG(:,bk); - Qp{ii}.q = allsource(ii,:); + smthlead = UL*q; %% THIS IS WHERE THE SMOOTHNESS GETS ADDED + normpower = 1/(smthlead'*smthlead); + Sourcepower(bk) = 1/(smthlead'*InvCov*smthlead); + allsource(bk) = Sourcepower(bk)./normpower; end - + allsource = allsource/max(allsource); + + + %% now get local maxima on mesh + M0.vert=vert; + M0.faces=face; + + Ip = spm_mesh_get_lm(M0,allsource); %% get local maxima + figure; + plot(allsource); + legend('sparse EBB'); + hold on; + maxBFpatch=40; + fprintf('Limiting to a max of %d peaks\n',maxBFpatch); + + [vals,ind]=sort(allsource(Ip),'descend'); + Ip=Ip(ind(1:maxBFpatch)); + plot(Ip,allsource(Ip),'ro'); + + + Qp = {}; + LQpL = {}; + for i = 1:maxBFpatch + % Patch locations determined by Ip + %-------------------------------------------------------------- + q = QG(:,Ip(i)); + Qp{end + 1}.q = q; + LQpL{end + 1}.q = UL*q; + end; %% end of sparse ebb + + + + case {'LOR','COH'} % create minimum norm prior %------------------------------------------------------------------ @@ -560,11 +575,11 @@ Qp{2} = QG; LQpL{2} = UL*Qp{2}*UL'; - case {'IID','MMN'} - % create minimum norm prior - %------------------------------------------------------------------ - Qp{1} = speye(Ns,Ns); - LQpL{1} = UL*UL'; + case {'IID','MMN'} + % create minimum norm prior + %------------------------------------------------------------------ + Qp{1} = speye(Ns,Ns); + LQpL{1} = UL*UL'; end fprintf('Using %d spatial source priors provided\n',length(Qp)); @@ -582,7 +597,7 @@ %-------------------------------------------------------------------------- switch(type) - case {'MSP','GS','EBBgs'} + case {'MSP','GS'} % Greedy search over MSPs %------------------------------------------------------------------ Np = length(Qp); @@ -595,7 +610,7 @@ % Multivariate Bayes (Here is performed the inversion) %------------------------------------------------------------------ - MVB = spm_mvb(AY,UL,[],Q,Qe,16); %% Qe is identity with unit trace + MVB = spm_mvb(AY,UL,[],Q,Qe,16); %% Qe is identity with unit trace % Accumulate empirical priors (New set of patches for the second inversion) %------------------------------------------------------------------ @@ -604,18 +619,19 @@ QP{end + 1} = sum(Qcp.*Q,2); LQP{end + 1} = (UL*Qcp)*Q'; LQPL{end + 1} = LQP{end}*UL'; - + end switch(type) - case {'MSP','ARD'} + case {'MSP','ARD','EBBsparse'} % ReML / ARD inversion %------------------------------------------------------------------ - [Cy,h,Ph,F] = spm_sp_reml(AYYA,[],[Qe LQpL],1); + %[Cy,h,Ph,F] = spm_sp_reml(AYYA,[],[Qe LQpL],1); + [Cy,h,Ph,F] = spm_sp_reml(AYYA,[],[Qe LQpL],Nn); % Spatial priors (QP) @@ -649,7 +665,7 @@ - [Cy,h,Ph,F] = spm_reml_sc(AYYA,[],[Qe LQpL],1,-4,16,Q0); + [Cy,h,Ph,F] = spm_reml_sc(AYYA,[],[Qe LQpL],Nn,-4,16,Q0); % Spatial priors (QP) %------------------------------------------------------------------ @@ -694,11 +710,11 @@ end; -[Cy,h,Ph,F]= spm_reml_sc(AYYA,[],Q,1,-4,16,Q0); +[Cy,h,Ph,F]= spm_reml_sc(AYYA,[],Q,Nn,-4,16,Q0); -%% recalculate F here +% recalculate F here Cp = sparse(0); LCp = sparse(0); @@ -776,7 +792,7 @@ inverse.VE = R2*VE; % variance explained inverse.woi = w; % time-window inverted inverse.Ip=Ip; %% patch locations - +inverse.Nn=Nn inverse.modality = modalities; % modalities inverted diff --git a/spm_eeg_invert_prepro.m b/spm_eeg_invert_prepro.m index 47679839..447e23d2 100644 --- a/spm_eeg_invert_prepro.m +++ b/spm_eeg_invert_prepro.m @@ -1,10 +1,10 @@ function [D] = spm_eeg_invert_prepro(D,val) -% Preprocessing for inversion stage -% Includes spatial and temporal dimension reduction. % -% This version only handles single subject single modality data. -% The removal of many scaling factors makes it easier to compare between -% forward models. +%% Preprocessing for inversion stage. +%% includes spatial and temporal dimension reduction +% +%% this version only handles single subject single modality data +%% the removal of many scaling factors makes it easier to compare between forward models % ReML inversion of multiple forward models for EEG-MEG % FORMAT [D] = spm_eeg_invert_classic(D) % ReML estimation of regularisation hyperparameters using the @@ -22,11 +22,11 @@ % 'LOR' LORETA-like model % 'IID' minimum norm % 'EBB' for empirical bayes beamformer -% + % inverse.priors{} - a cell array of anatomical and functional priors to be % considered -% -% +%% + % inverse.woi - time window of interest ([start stop] in ms) % inverse.lpf - band-pass filter - low frequency cut-off (Hz) % inverse.hpf - band-pass filter - high frequency cut-off (Hz) @@ -41,10 +41,10 @@ % inverse.Na - number of most energetic dipoles % inverse.sdv - standard deviations of Gaussian temporal correlation % inverse.Qe - any sensor error components (e.g. empty-room data) -% inverse.Qe0 - minimum amount of sensor noise power relative to -% signal eg 0.1 would correspond to power SNR of 10.0 -% inverse.A - predefined spatial modes (Nchans*Nmodes) to project -% sensor data through +% inverse.Qe0 - minimum amount of sensor noise power relative to +% signal eg 0.1 would correspond to power SNR of 10.0 +% inverse.A - predefined spatial modes (Nchans*Nmodes) to project +% sensor data through % % Evaluates: % @@ -73,8 +73,8 @@ % contains none of the associated scaling factors. % No symmetric priors are used in this implementation (just single patches) % There is an option for a Beamforming prior : inversion type 'EBB' -% also added new beamforming method- using GS rather than ARD, from Juan -% David Martinez Vargas ('EBBgs'). +% also added new beamforming method- using GS rather than ARD- from Juan +% David Martinez Vargas 'EBBgs' % % The code was used in % Lopez, J. D., Penny, W. D., Espinosa, J. J., Barnes, G. R. (2012). @@ -83,17 +83,17 @@ % Neuroimage 60(2), 1194-1204 doi:10.1016/j.neuroimage.2012.01.077. %__________________________________________________________________________ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging - + % Jose David Lopez, Gareth Barnes, Vladimir Litvak -% $Id: spm_eeg_invert_prepro.m 6444 2015-05-21 11:15:48Z guillaume $ +% $Id: spm_eeg_invert_prepro.m 6618 2015-12-01 16:25:38Z spm $ Nl = length(D); -if Nl > 1 +if Nl>1 error('function only defined for a single subject'); else - D = D{1}; + D=D{1}; end % D - SPM data structure @@ -105,7 +105,7 @@ end -val = D.val; +val=D.val; inverse = D.inv{val}.inverse; @@ -119,20 +119,20 @@ try, type = inverse.type; catch, type = 'GS'; end try, s = inverse.smooth; catch, s = 0.6; end try, Np = inverse.Np; catch, Np = 256; end -try, Nr = inverse.Nr; catch, Nr = 16; end % requested number of temporal modes, could be changed depending on svd +try, Nr = inverse.Nr; catch, Nr = 16; end %% requested number of temporal modes, could be changed depending on svd try, xyz = inverse.xyz; catch, xyz = [0 0 0]; end try, rad = inverse.rad; catch, rad = 128; end -try, hpf = inverse.hpf; catch, hpf = 48; end % need to one day put these the correct way round +try, hpf = inverse.hpf; catch, hpf = 48; end %% need to one day put these the correct way round try, lpf = inverse.lpf; catch, lpf = 0; end try, sdv = inverse.sdv; catch, sdv = 4; end try, Han = inverse.Han; catch, Han = 1; end try, woi = inverse.woi; catch, woi = []; end try, Nm = inverse.Nm; catch, Nm = []; end -try, Nt = inverse.Nt; catch, Nt = []; end % fixed number of temporal modes +try, Nt = inverse.Nt; catch, Nt = []; end %% fixed number of temporal modes try, Ip = inverse.Ip; catch, Ip = []; end -try, QE = inverse.QE; catch, QE = 1; end % empty room noise measurement -try, Qe0 = inverse.Qe0; catch, Qe0 = exp(-5); end % set noise floor at 1/100th signal power i.e. assume amplitude SNR of 10 -try, inverse.A; catch, inverse.A = []; end % orthogonal channel modes +try, QE = inverse.QE; catch, QE=1; end % empty room noise measurement +try, Qe0 = inverse.Qe0; catch, Qe0 = exp(-5); end %% set noise floor at 1/100th signal power i.e. assume amplitude SNR of 10 +try, inverse.A; catch, inverse.A = []; end %% orthogonal channel modes @@ -155,14 +155,17 @@ [L,D] = spm_eeg_lgainmat(D); % Generate/load lead field Nd=size(L,2); + + % Check gain or lead-field matrices -%-------------------------------------------------------------------------- +%------------------------------------------------------------------ -if size(modalities,1)>1 +if size(modalities,1)>1, error('not defined for multiple modalities'); -end -Ic = setdiff(D.indchantype(modalities), badchannels(D)); -Nd = size(L,2); % Number of dipoles +end; + +Ic{1} = setdiff(D.indchantype(modalities), badchannels(D)); +Nd = size(L,2); % Number of dipoles @@ -174,11 +177,11 @@ fprintf('Optimising and aligning spatial modes ...\n') % eliminate low SNR spatial modes -%-------------------------------------------------------------------------- -disp('FIXING FULL RANK A'); -inverse.A=eye(length(Ic)); Nm=size(inverse.A,1); +%------------------------------------------------------------------ +%disp('FIXING FULL RANK A'); +%inverse.A=eye(length(Ic)); Nm=size(inverse.A,1); -if isempty(inverse.A) % no spatial modes prespecified +if isempty(inverse.A), % no spatial modes prespecified if isempty(Nm), %% number of modes not specifiedd [U,ss,vv] = spm_svd((L*L'),exp(-16)); A = U'; % spatial projector A @@ -193,11 +196,11 @@ end; - ss = ss(1:Nm); + ss=ss(1:Nm); disp('using preselected number spatial modes !'); A = U(:,1:Nm)'; % spatial projector A UL = A*L; - end + end; else %% U was specified in input disp('Using pre-specified spatial modes'); if isempty(Nm), @@ -206,7 +209,7 @@ % A=inverse.A; UL=A*L; -end +end; Nm = size(UL,1); % Number of spatial projectors @@ -214,7 +217,7 @@ clear ss vv % Report -%-------------------------------------------------------------------------- +%---------------------------------------------------------------------- fprintf('Using %d spatial modes',Nm) @@ -225,13 +228,13 @@ AYYA = 0; % pooled response for ReML % Time-window of interest -%-------------------------------------------------------------------------- +%---------------------------------------------------------------------- if isempty(woi) w = 1000*[min(D.time) max(D.time)]; else w=woi; %% in milliseconds -end +end; It = (w/1000 - D.timeonset)*D.fsample + 1; It = max(1,It(1)):min(It(end), length(D.time)); @@ -241,7 +244,7 @@ % Peristimulus time -%-------------------------------------------------------------------------- +%---------------------------------------------------------------------- pst = 1000*D.time; % peristimulus time (ms) pst = pst(It); % windowed time (ms) dur = (pst(end) - pst(1))/1000; % duration (s) @@ -249,13 +252,13 @@ Nb = length(It); % number of time bins % Serial correlations -%-------------------------------------------------------------------------- +%---------------------------------------------------------------------- K = exp(-(pst - pst(1)).^2/(2*sdv^2)); %% sdv set to 4 by default K = toeplitz(K); qV = sparse(K*K'); %% Samples* samples covariance matrix- assumes smooth iid % Confounds and temporal subspace -%-------------------------------------------------------------------------- +%---------------------------------------------------------------------- T = spm_dctmtx(Nb,Nb); % use plot(T) here! @@ -263,19 +266,19 @@ T = T(:,j); % Apply the filter to discrete cosines dct = dct(j); % Frequencies accepted -% Hanning window -%-------------------------------------------------------------------------- +%% Hanning window +%---------------------------------------------------------------------- if Han W = sparse(1:Nb,1:Nb,spm_hanning(Nb)); %% use hanning unless specified else W=1; -end +end; % get trials or conditions -%-------------------------------------------------------------------------- +%---------------------------------------------------------------------- try trial = D.inv{D.val}.inverse.trials; catch @@ -283,7 +286,7 @@ end Ntrialtypes=length(trial); % get temporal covariance (Y'*Y) to find temporal modes -%========================================================================== +%====================================================================== YY = 0; @@ -291,10 +294,11 @@ badtrialind=D.badtrials; for j = 1:Ntrialtypes, % pool over conditions c = D.indtrial(trial{j}); % and trials + c=setxor(c,badtrialind); %% ignore bad trials Nk = length(c); for k = 1:Nk - Y = A*D(Ic,It,c(k)); + Y = A*D(Ic{1},It,c(k)); YY = YY + Y'*Y; N = N + 1; @@ -304,28 +308,28 @@ % Apply any Hanning and filtering -%-------------------------------------------------------------------------- +%------------------------------------------------------------------ YY = W'*YY*W; % Hanning -YTY = T'*YY*T; % Filter +YTY = T'*YY*T; % Filter -%========================================================================== +%====================================================================== if isempty(Nt), %% automatically assign appropriate number of temporal modes - [U,E] = spm_svd(YTY,exp(-8)); % get temporal modes + [U E] = spm_svd(YTY,exp(-8)); % get temporal modes if isempty(U), %% fallback warning('nothing found using spm svd, using svd'); - [U,E] = svd(YTY); % get temporal modes - end + [U E] = svd(YTY); % get temporal modes + end; E = diag(E)/trace(YTY); % normalise variance Nr = min(length(E),Nmax); % number of temporal modes Nr=max(Nr,1); %% use at least one mode else %% use predefined number of modes - [U,E] = svd(YTY); % get temporal modes + [U E] = svd(YTY); % get temporal modes E = diag(E)/trace(YTY); % normalise variance disp('Fixed number of temporal modes'); Nr=Nt; -end +end; V = U(:,1:Nr); % temporal modes VE = sum(E(1:Nr)); % variance explained @@ -334,7 +338,7 @@ fprintf('accounting for %0.2f percent average variance\n',full(100*VE)) % projection and whitening -%-------------------------------------------------------------------------- +%---------------------------------------------------------------------- S = T*V; % temporal projector Vq = S*pinv(S'*qV*S)*S'; % temporal precision @@ -342,83 +346,86 @@ %S=eye(size(YY,1)); % get spatial covariance (Y*Y') for Gaussian process model -%========================================================================== +%====================================================================== % loop over Ntrialtypes trial types -%-------------------------------------------------------------------------- -UYYU = 0; -AYYA = 0; -Nn = 0; % number of samples -AY = {}; -Ntrials = 0; +%---------------------------------------------------------------------- +UYYU = 0; +AYYA=0; +Nn =0; % number of samples +AY={}; +Ntrials=0; -for j = 1:Ntrialtypes +for j = 1:Ntrialtypes, UY{j} = sparse(0); - c = D.indtrial(trial{j}); - c = setxor(c,badtrialind); % ignore bad trials + c = D.indtrial(trial{j}); + c=setxor(c,badtrialind); %% ignore bad trials Nk = length(c); % loop over epochs - %---------------------------------------------------------------------- + %------------------------------------------------------------------ for k = 1:Nk % stack (scaled aligned data) over modalities - %------------------------------------------------------------------ + %-------------------------------------------------------------- - Y = D(Ic,It,c(k))*S; % in temporal subspace - Y = A*Y; % in spatial subspace + Y = D(Ic{1},It,c(k))*S; %% in temporal subspace + Y=A*Y; %% in spatial subspace % accumulate first & second-order responses - %------------------------------------------------------------------ - Nn = Nn + Nr; % number of samples + %-------------------------------------------------------------- + Nn = Nn + Nr; % number of samples - YY = Y*Y'; % and covariance - Ntrials = Ntrials+1; + YY = Y*Y'; % and covariance + Ntrials=Ntrials+1; % accumulate statistics (subject-specific) - %------------------------------------------------------------------ - UY{j} = UY{j} + Y; % condition-specific ERP - UYYU = UYYU + YY; % subject-specific covariance + %-------------------------------------------------------------- + UY{j} = UY{j} + Y; % condition-specific ERP + UYYU = UYYU + YY; % subject-specific covariance % and pool for optimisation of spatial priors over subjects - %------------------------------------------------------------------ + %-------------------------------------------------------------- AY{end + 1} = Y; % pooled response for MVB AYYA = AYYA + YY; % pooled response for ReML end end -AY = spm_cat(AY); % goes to MVB/GS algorithm +AY=spm_cat(AY); %% goes to MVB/GS algorithm + +ID = spm_data_id(AY); %% get a unique ID for these filtered data + -ID = spm_data_id(AY); % get a unique ID for these filtered data %====================================================================== -inverse.AY = AY; %% concatenated data in reduced spatial modes -inverse.AYYA = AYYA;%% sensor level covariance of all trials, conditions and samples (containing Nn temporal modes in total) -%inverse.type = type; % inverse model -%inverse.Y = Y; % ERP data (reduced) -inverse.UY = UY; % ERP data (reduced) -inverse.L = UL; % Lead-field (reduced) +inverse.AY=AY; %% concatenated data in reduced spatial modes +inverse.AYYA=AYYA;%% sensor level covariance of all trials, conditions and samples (containing Nn temporal modes in total) +%inverse.type = type; % inverse model +%inverse.Y = Y; % ERP data (reduced) +inverse.UY = UY; % ERP data (reduced) +inverse.L = UL; % Lead-field (reduced) inverse.Nn=Nn; %% number of independent samples (temporal modes* trials* conditions) -inverse.qV = Vq; % temporal correlations -inverse.T = S; % temporal projector -inverse.U = {A}; % spatial projector -%inverse.Is = Is; % Indices of active dipoles +inverse.qV = Vq; % temporal correlations +inverse.T = S; % temporal projector +inverse.U = {A}; % spatial projector +%inverse.Is = Is; % Indices of active dipoles +inverse.Ic=Ic; %% good channels +inverse.It=It; %% time indices +inverse.Nd = Nd; % number of dipoles +inverse.pst = pst; % peristimulus time +inverse.dct = dct; % frequency range -inverse.Nd = Nd; % number of dipoles -inverse.pst = pst; % peristimulus time -inverse.dct = dct; % frequency range +inverse.ID = ID; % data ID +inverse.woi = w; % time-window inverted -inverse.ID = ID; % data ID -inverse.woi = w; % time-window inverted - -inverse.modality = modalities; % modalities inverted +inverse.modality = modalities; % modalities inverted % save in struct @@ -427,3 +434,9 @@ D.inv{val}.method = 'Imaging'; disp('Done invert prepro'); + + + + + +return diff --git a/spm_eeg_invert_setuppatches.m b/spm_eeg_invert_setuppatches.m index 8d33f452..273aea96 100644 --- a/spm_eeg_invert_setuppatches.m +++ b/spm_eeg_invert_setuppatches.m @@ -18,7 +18,7 @@ % Copyright (C) 2010 Wellcome Trust Centre for Neuroimaging % Gareth Barnes -% $Id: spm_eeg_invert_setuppatches.m 6458 2015-05-27 16:22:09Z spm $ +% $Id: spm_eeg_invert_setuppatches.m 6498 2015-07-15 19:13:31Z gareth $ Npatchiter=size(allIp,1); @@ -92,7 +92,8 @@ % NOW MAYBE WRITE A NEW PATCH FILE - priorfname=[priordir filesep sprintf('prior%d.mat',k+priorcount)]; + idnum=randi(1e6); + priorfname=[priordir filesep sprintf('prior%d.mat',idnum)]; fprintf('Saving %s\n',priorfname); F=[]; % no associated free energy value allpriornames=strvcat(allpriornames,priorfname); diff --git a/spm_eeg_merge.m b/spm_eeg_merge.m index 7eef7f29..9193d17c 100644 --- a/spm_eeg_merge.m +++ b/spm_eeg_merge.m @@ -58,9 +58,9 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % % Stefan Kiebel, Vladimir Litvak, Doris Eckstein, Rik Henson -% $Id: spm_eeg_merge.m 6298 2015-01-05 12:18:23Z vladimir $ +% $Id: spm_eeg_merge.m 6622 2015-12-03 11:54:13Z vladimir $ -SVNrev = '$Rev: 6298 $'; +SVNrev = '$Rev: 6622 $'; %-Startup %-------------------------------------------------------------------------- @@ -132,15 +132,15 @@ end if ~isempty(D{i}.sensors('MEG')) - megsens = [megsens D{i}.sensors('MEG')]; + megsens = spm_cat_struct(megsens, D{i}.sensors('MEG')); end if ~isempty(D{i}.sensors('EEG')) - eegsens = [eegsens D{i}.sensors('EEG')]; + eegsens = spm_cat_struct(eegsens, D{i}.sensors('EEG')); end if ~isempty(megsens) || ~isempty(eegsens) - fid = [fid D{i}.fiducials]; + fid = spm_cat_struct(fid, D{i}.fiducials); end Ntrials = [Ntrials D{i}.ntrials]; @@ -348,6 +348,7 @@ % merged file Dout = repl(Dout, find(Find == i), D{i}.repl); Dout = trialonset(Dout, find(Find == i), D{i}.trialonset); + Dout = trialtag(Dout, find(Find == i), D{i}.trialtag); Dout = events(Dout, find(Find == i), D{i}.events); if ismember(i, Ibar), spm_progress_bar('Set', i); end diff --git a/spm_eeg_planarchannelset.m b/spm_eeg_planarchannelset.m index 79545225..3fbda572 100644 --- a/spm_eeg_planarchannelset.m +++ b/spm_eeg_planarchannelset.m @@ -1,20 +1,18 @@ -function [planar] = spm_eeg_planarchannelset(input) - -% FUNCTION that defines the planar gradiometer channel combinations -% The output cell-array contains the horizontal label, vertical label -% and the label after combining the two. -% -% Use as -% [planar] = spm_eeg_planarchannelset(data) +function planar = spm_eeg_planarchannelset(data) +% Define the planar gradiometer channel combinations +% FORMAT planar = spm_eeg_planarchannelset(data) % -% Copyright (C) 2014 Wellcome Trust Centre for Neuroimaging +% The output cell array contains the horizontal label, vertical label and +% the label after combining the two. +%__________________________________________________________________________ +% Copyright (C) 2013-2015 Wellcome Trust Centre for Neuroimaging + +% Vladimir Litvak +% $Id: spm_eeg_planarchannelset.m 6638 2015-12-10 15:43:57Z guillaume $ -% $Id: spm_eeg_planarchannelset.m 5936 2014-04-01 09:40:26Z vladimir $ -if isa(input, 'cell') && ~isempty(input) && isa(input{1}, 'char') - data.label = input; -else - data = input; +if isa(data, 'cell') && ~isempty(data) && isa(data{1}, 'char') + data = struct('label',data); end planar = ft_senslabel(lower(ft_senstype(data)), 'output', 'planarcombined'); diff --git a/spm_eeg_reduce_pca.m b/spm_eeg_reduce_pca.m index ea76212c..a84068de 100644 --- a/spm_eeg_reduce_pca.m +++ b/spm_eeg_reduce_pca.m @@ -16,7 +16,7 @@ % Copyright (C) 2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_eeg_reduce_pca.m 5528 2013-06-07 11:47:27Z vladimir $ +% $Id: spm_eeg_reduce_pca.m 6535 2015-08-25 11:45:26Z vladimir $ if nargin == 0 @@ -40,6 +40,8 @@ D = S.D; +isTF = strncmp(D.transformtype, 'TF', 2); + YY = 0; ns = 0; @@ -51,10 +53,15 @@ else Ibar = 1:ntrials; end for i = 1:ntrials - Y = squeeze(D(S.chanind, :, i)); + if isTF + Y = squeeze(D(S.chanind, :, :, i)); + Y = reshape(Y, size(Y, 1), []); + else + Y = squeeze(D(S.chanind, :, i)); + end Y = detrend(Y', 'constant'); YY = YY+(Y'*Y); - ns = ns + D.nsamples-1; + ns = ns + size(Y, 2) - 1; if ismember(i, Ibar) spm_progress_bar('Set', i); drawnow; end diff --git a/spm_eeg_remove_bad_trials.m b/spm_eeg_remove_bad_trials.m index 13ef29e4..6c49c3e7 100644 --- a/spm_eeg_remove_bad_trials.m +++ b/spm_eeg_remove_bad_trials.m @@ -17,9 +17,9 @@ % Copyright (C) 2008-2012 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_eeg_remove_bad_trials.m 5079 2012-11-25 18:38:18Z vladimir $ +% $Id: spm_eeg_remove_bad_trials.m 6526 2015-08-20 10:28:36Z vladimir $ -SVNrev = '$Rev: 5079 $'; +SVNrev = '$Rev: 6526 $'; %-Startup %-------------------------------------------------------------------------- @@ -77,6 +77,7 @@ Dnew = repl(Dnew, ':', repl(D, goodtrials)); Dnew = events(Dnew, ':', events(D, goodtrials)); Dnew = trialonset(Dnew, ':', trialonset(D, goodtrials)); +Dnew = trialtag(Dnew, ':', trialtag(D, goodtrials)); %-Save the new M/EEG dataset %-------------------------------------------------------------------------- diff --git a/spm_eeg_render.m b/spm_eeg_render.m index b5d8feb9..4bd6277a 100644 --- a/spm_eeg_render.m +++ b/spm_eeg_render.m @@ -1,56 +1,53 @@ -function [out] = spm_eeg_render(m,options) +function [out] = spm_eeg_render(m,options) % Visualisation routine for the cortical surface % FORMAT [out] = spm_eeg_render(m,options) % % INPUT: -% - m = MATLAB mesh (containing the fields .faces et .vertices) or GIFTI -% format file. -% - options = structure variable: -% .texture = texture to be projected onto the mesh -% .clusters = cortical parcelling (cell variable containing the -% vertex indices of each cluster) -% .clustersName = name of the clusters -% .figname = name to be given to the window -% .ParentAxes = handle of the axes within which the mesh should be -% displayed -% .hfig = handle of existing figure. If this option is provided, then -% visu_maillage_surf adds the (textured) mesh to the figure hfig, and -% a control for its transparancy. +% m - patch structure (with fields .faces et .vertices) +% or GIFTI format filename +% options - structure with optional fields: +% .texture - texture to be projected onto the mesh +% .clusters - cortical parcellation (cell variable containing +% the vertex indices of each cluster) +% .clustersName - name of the clusters +% .figname - name to be given to the figure +% .ParentAxes - handle of the axes within which the mesh should +% be displayed +% .hfig - handle of existing figure. If this option is +% provided, then spm_eeg_render adds the (textured) +% mesh to the figure hfig, and a control for its +% transparency. % % OUTPUT: -% - out: a structure containing the fields: -% .hfra: frame structure for movie building -% .handles: a structure containing the handles of the created -% uicontrols and mesh objects. -% .m: the structure used to create the mesh +% out - structure with fields: +% .hfra - frame structure for movie building +% .handles - structure containing the handles of the created +% uicontrols and mesh objects +% .m - the structure used to create the mesh. %__________________________________________________________________________ % -% This function is a visualization routine, mainly for texture and +% This function is a visualisation routine, mainly for texture and % clustering on the cortical surface. -% NB: The texture and the clusters can not be visualized at the same time. +% NB: The texture and the clusters cannot be visualised at the same time. %__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging % Jean Daunizeau -% $Id: spm_eeg_render.m 4818 2012-07-31 14:53:10Z guillaume $ +% $Id: spm_eeg_render.m 6528 2015-08-21 11:48:54Z guillaume $ %----------------------------------------------------------------------% -%------------- Common features for any visualization ------------------% +%------------- Common features for any visualisation ------------------% %----------------------------------------------------------------------% % Check mesh format try - if ischar(m) && exist(m,'file')==2 - try m = gifti(m);end + if ~isstruct(m) + m = export(gifti(m),'patch'); end - m0.faces = m.faces; - m0.vertices = m.vertices; - m = m0; - clear m0; catch - disp('spm_eeg_render: unknown mesh format!') - return + if ischar(m), fprintf('File: %s\n',m); end + error('Unknown mesh format.') end @@ -59,8 +56,8 @@ 'visible','off',... 'color',ones(1,3),... 'NumberTitle','Off',... - 'Name','Mesh visualization',... - 'tag','visu_maillage_surf'); + 'Name','Mesh visualisation',... + 'tag','spm_eeg_render'); ns = 0; texture = 'none'; clusters = 'none'; diff --git a/spm_eeg_spatial_confounds.m b/spm_eeg_spatial_confounds.m index 380811fb..94068dcc 100644 --- a/spm_eeg_spatial_confounds.m +++ b/spm_eeg_spatial_confounds.m @@ -13,10 +13,10 @@ % Copyright (C) 2008-2014 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_eeg_spatial_confounds.m 6437 2015-05-14 12:27:21Z vladimir $ +% $Id: spm_eeg_spatial_confounds.m 6625 2015-12-03 21:49:24Z vladimir $ -SVNrev = '$Rev: 6437 $'; +SVNrev = '$Rev: 6625 $'; %-Startup %-------------------------------------------------------------------------- @@ -29,6 +29,9 @@ D = spm_eeg_load(S.D); +if ~isfield(S, 'conditions') || isempty(S.conditions), S.conditions = D.condlist; end +if ~iscell(S.conditions), S.conditions = {S.conditions}; end + sconf = []; switch upper(S.mode) case 'EYES' @@ -111,7 +114,8 @@ cl = D.condlist; svdinput = []; for i = 1:numel(cl) - svdinput = [svdinput mean(D(D.indchantype('MEEG', 'GOOD'), D.indsample(1e-3*S.timewin(1)):D.indsample(1e-3*S.timewin(2)), D.indtrial(cl{i})), 3)]; + tmp = D(D.indchantype('MEEG', 'GOOD'), D.indsample(1e-3*S.timewin(1)):D.indsample(1e-3*S.timewin(2)), D.indtrial(cl{i})); + svdinput = [svdinput, reshape(tmp, size(tmp, 1), [])]; end [U, L, V] = spm_svd(svdinput); diff --git a/spm_eeg_specest_mtmconvol.m b/spm_eeg_specest_mtmconvol.m index b45e96a6..8559e0f2 100644 --- a/spm_eeg_specest_mtmconvol.m +++ b/spm_eeg_specest_mtmconvol.m @@ -25,7 +25,7 @@ %______________________________________________________________________________________ % Copyright (C) 2011-2013 Wellcome Trust Centre for Neuroimaging -% $Id: spm_eeg_specest_mtmconvol.m 6047 2014-06-16 11:11:22Z vladimir $ +% $Id: spm_eeg_specest_mtmconvol.m 6612 2015-11-27 18:43:15Z vladimir $ %-This part if for creating a config branch that plugs into spm_cfg_eeg_tf @@ -135,6 +135,10 @@ [spectrum,ntaper,freqoi,timeoi] = ft_specest_mtmconvol(data, time, 'taper', S.taper, 'timeoi', timeoi, 'freqoi', S.frequencies,... 'timwin', repmat(timeres, 1, length(S.frequencies)), 'tapsmofrq', freqres, 'pad', pad, 'verbose', 0); +% To prevent differences between files due to numerical imprecision +if length(freqoi)==length(S.frequencies) && max(abs(freqoi-S.frequencies))<0.1 + freqoi = S.frequencies; +end % This in principle should not happen if length(unique(diff(timeoi)))>1 && max(abs(diff(unique(diff(timeoi)))))>1e-6 diff --git a/spm_existfile.mexw32 b/spm_existfile.mexw32 index f8a85515..116428a9 100755 Binary files a/spm_existfile.mexw32 and b/spm_existfile.mexw32 differ diff --git a/spm_existfile.mexw64 b/spm_existfile.mexw64 index 94f91a70..f6a0f27d 100755 Binary files a/spm_existfile.mexw64 and b/spm_existfile.mexw64 differ diff --git a/spm_field.mexw32 b/spm_field.mexw32 index a20a8b15..65798f69 100755 Binary files a/spm_field.mexw32 and b/spm_field.mexw32 differ diff --git a/spm_field.mexw64 b/spm_field.mexw64 index d0682e19..511dc7af 100755 Binary files a/spm_field.mexw64 and b/spm_field.mexw64 differ diff --git a/spm_fieldindices.m b/spm_fieldindices.m index 1793ad73..0195f95d 100644 --- a/spm_fieldindices.m +++ b/spm_fieldindices.m @@ -1,5 +1,5 @@ function [ix] = spm_fieldindices(X,varargin) -% Return the indices of fields in a structure (and vice versa) +% Returns the indices of fields in a structure (and vice versa) % FORMAT [i] = spm_fieldindices(X,field1,field2,...) % FORMAT [field] = spm_fieldindices(X,i) % @@ -12,7 +12,7 @@ % Copyright (C) 2010-2013 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_fieldindices.m 6427 2015-05-05 15:42:35Z karl $ +% $Id: spm_fieldindices.m 6532 2015-08-23 13:59:19Z karl $ % if varargin is a vector simply return fieldnames @@ -49,7 +49,7 @@ catch try - % field is the expression + % field is an expression %---------------------------------------------------------- eval(['x.' field ' = x.' field ' + 1;']); ix = ix + spm_vec(x); diff --git a/spm_find_pC.m b/spm_find_pC.m index cd042662..de53998a 100644 --- a/spm_find_pC.m +++ b/spm_find_pC.m @@ -1,6 +1,7 @@ -function [i,pC,pE,Np] = spm_find_pC(pC,pE,fields) +function [i,pC,pE,Np] = spm_find_pC(varargin) % Utility routine that finds the indices of non-zero covariance % FORMAT [i,pC,pE,Np] = spm_find_pC(pC,pE,fields) +% FORMAT [i,pC,pE,Np] = spm_find_pC(DCM,fields) % FORMAT [i,pC,pE,Np] = spm_find_pC(DCM) % % pC - covariance matrix or variance stucture @@ -19,17 +20,31 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_find_pC.m 6427 2015-05-05 15:42:35Z karl $ +% $Id: spm_find_pC.m 6529 2015-08-21 13:27:38Z karl $ - -%-get pC from DCM structure +%-parse input arguments %-------------------------------------------------------------------------- -if ischar(pC) - pC = load(pC,'DCM'); - pC = pC.DCM; +if nargin > 2 + pC = varargin{1}; + pE = varargin{2}; + fields = varargin{3}; +elseif numel(varargin) > 1 + DCM = varargin{1}; + fields = varargin{2}; +else + DCM = varargin{1}; end -if any(isfield(pC,{'options','M'})) - try, [pC,pE] = spm_find_rC(pC); end + +%-get prior density from DCM +%-------------------------------------------------------------------------- +if nargin < 3 + if ischar(DCM) + DCM = load(DCM,'DCM'); + DCM = DCM.DCM; + end + if any(isfield(DCM,{'options','M'})) + try, [pC,pE] = spm_find_rC(DCM); end + end end %-Deal with variance structures @@ -47,7 +62,7 @@ %-subsample fields if necessary %-------------------------------------------------------------------------- -if nargin > 2 +if nargin > 1 if ischar(fields), fields = {fields}; end if isstruct(pE) j = spm_fieldindices(pE,fields{:}); diff --git a/spm_fmri_concatenate.m b/spm_fmri_concatenate.m index 83088d12..7aaa77bc 100644 --- a/spm_fmri_concatenate.m +++ b/spm_fmri_concatenate.m @@ -1,24 +1,20 @@ function spm_fmri_concatenate(P, scans) -% Adjust an SPM.mat which has concatenated sessions to improve accuracy +% Adjust an SPM.mat which has concatenated sessions. % FORMAT spm_post_concatenate(P, scans) -% The high pass filter will be re-specified as if sessions are separate. +% Session regressors are added and the high-pass filter and non-sphericity +% estimates adjusted as if sessions are separate. % % P - filename of the SPM.mat file to adjust % scans - [1 x n] vector with the original number of scans in each session % % The expected workflow is: % -% 1. Manually specify a GLM with additional unconvolved regressors -% for each session, except for the last session. +% 1. Manually specify a GLM with timeseries and onsets concatenated % 2. Run spm_post_concatenate on the saved SPM.mat. % 3. Estimate the SPM.mat in the normal way. % % Tips: % -% - The session regressors should be binary vectors. E.g. a regressor named -% 'sess1' would include 1s for every scan in session 1 and zeros -% elsewhere. -% % - The BOLD-response may overhang from one session to the next. To reduce % this, acquire additional volumes at the end of each session and / or % add regressors to model the trials at the session borders. @@ -26,7 +22,7 @@ function spm_fmri_concatenate(P, scans) % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin & Peter Zeidman -% $Id: spm_fmri_concatenate.m 6459 2015-05-27 16:54:53Z peter $ +% $Id: spm_fmri_concatenate.m 6533 2015-08-24 10:57:35Z peter $ %-Input parameters diff --git a/spm_fs_fmri_csd.m b/spm_fs_fmri_csd.m index bac1aa4d..bfe3ccef 100644 --- a/spm_fs_fmri_csd.m +++ b/spm_fs_fmri_csd.m @@ -13,10 +13,12 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_fs_fmri_csd.m 5892 2014-02-23 11:00:16Z karl $ +% $Id: spm_fs_fmri_csd.m 6662 2016-01-08 15:20:02Z adeel $ % return (scaled) cross-spectra and covariance functions %-------------------------------------------------------------------------- c = spm_csd2ccf(y,M.Hz); -y = [y/16; c(1:8:end,:,:)*2]; +% y = [y; c(1:8:end,:,:)*16]; +idx = round(length(c(:,:,1))/2); +y = [y; c(idx-5:idx+5,:,:)*16]; diff --git a/spm_funcheck.m b/spm_funcheck.m index a2518c55..ccffff5a 100644 --- a/spm_funcheck.m +++ b/spm_funcheck.m @@ -8,7 +8,7 @@ % Copyright (C) 2013 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_funcheck.m 5664 2013-10-01 18:39:05Z spm $ +% $Id: spm_funcheck.m 6481 2015-06-16 17:01:47Z karl $ %-Create function handle @@ -16,7 +16,7 @@ % if f is already a function handle %-------------------------------------------------------------------------- -if isa(f,'function_handle') +if isa(f,'function_handle') || isempty(f) h = f; % if f is filename or expression diff --git a/spm_gamrnd.mexw32 b/spm_gamrnd.mexw32 index a2d707da..c76ebf9e 100755 Binary files a/spm_gamrnd.mexw32 and b/spm_gamrnd.mexw32 differ diff --git a/spm_gamrnd.mexw64 b/spm_gamrnd.mexw64 index fd282647..fd535626 100755 Binary files a/spm_gamrnd.mexw64 and b/spm_gamrnd.mexw64 differ diff --git a/spm_getSPM.m b/spm_getSPM.m index 978e4e93..cbfa15e5 100644 --- a/spm_getSPM.m +++ b/spm_getSPM.m @@ -182,7 +182,7 @@ % Copyright (C) 1999-2014 Wellcome Trust Centre for Neuroimaging % Andrew Holmes, Karl Friston & Jean-Baptiste Poline -% $Id: spm_getSPM.m 6314 2015-01-23 17:00:51Z guillaume $ +% $Id: spm_getSPM.m 6619 2015-12-01 19:20:22Z guillaume $ %-GUI setup @@ -713,7 +713,7 @@ case 'FDR' % False discovery rate %-------------------------------------------------------------- - if topoFDR, + if topoFDR fprintf('\n'); %-# error('Change defaults.stats.topoFDR to use voxel FDR'); end @@ -750,16 +750,20 @@ %-Compute p-values for topological and voxel-wise FDR (all search voxels) %---------------------------------------------------------------------- if ~topoFDR + %-Voxel-wise FDR + %------------------------------------------------------------------ fprintf('%s%30s',repmat(sprintf('\b'),1,30),'...for voxelFDR') %-# Ps = spm_z2p(Zum,df,STAT,n); - end - - %-Peak FDR - %---------------------------------------------------------------------- - if ~spm_mesh_detect(xCon(Ic(1)).Vspm) - [up,Pp] = spm_uc_peakFDR(0.05,df,STAT,R,n,Zum,XYZum,u); + up = spm_uc_FDR(0.05,df,STAT,n,sort(Ps(:))); + Pp = []; else - [up,Pp] = spm_uc_peakFDR(0.05,df,STAT,R,n,Zum,XYZum,u,G); + %-Peak FDR + %------------------------------------------------------------------ + if ~spm_mesh_detect(xCon(Ic(1)).Vspm) + [up,Pp] = spm_uc_peakFDR(0.05,df,STAT,R,n,Zum,XYZum,u); + else + [up,Pp] = spm_uc_peakFDR(0.05,df,STAT,R,n,Zum,XYZum,u,G); + end end %-Cluster FDR @@ -778,6 +782,11 @@ Pc = []; end + if ~topoFDR + uc = NaN; + Pc = []; + end + %-Peak FWE %---------------------------------------------------------------------- uu = spm_uc(0.05,df,STAT,R,n,S); @@ -856,8 +865,11 @@ end else - - k = 0; + try + k = xSPM.k; + catch + k = 0; + end end % (if ~isempty(XYZ)) diff --git a/spm_get_lm.m b/spm_get_lm.m index d9e83640..160b0063 100644 --- a/spm_get_lm.m +++ b/spm_get_lm.m @@ -1,24 +1,27 @@ -function varargout = spm_get_lm(varargin) +function idx = spm_get_lm(vol,list,n) % Identification of local maxima in 3(or 2)D volume - a compiled routine -% FORMAT idx = spm_get_lm(vol,list) +% FORMAT idx = spm_get_lm(vol,list,n) % % Routine that identifies which voxels in a list of coordinates that are % local maxima, and returns a list of indices into the coordinate list for % those maxima. % % Input: -% vol - 3(or 2)D volume of statistics (e.g. t or F) -% list - 3xn (or 2xn) list of voxel coordinates of tentative local -% maxima. +% vol - 3(or 2)D volume of statistics (e.g. t or F) +% list - 3xn (or 2xn) list of voxel coordinates of tentative local +% maxima. +% n - connectivity criterion: 6 (surface), 18 (edge) or 26 (corner). +% [Default: 18]. +% (for a 2D image these correspond to 4, 8 and 8 respectively). % % Output: -% idx - Index into list such that list(:,idx) returns those -% coordinates that are truly local maxima. +% idx - Index into list such that list(:,idx) returns those +% coordinates that are truly local maxima. %__________________________________________________________________________ -% Copyright (C) 2002-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2002-2015 Wellcome Trust Centre for Neuroimaging % Jesper Andersson -% $Id: spm_get_lm.m 6015 2014-05-23 15:46:19Z guillaume $ +% $Id: spm_get_lm.m 6534 2015-08-24 16:02:56Z guillaume $ %-This is merely the help file for the compiled routine error('spm_get_lm.c not compiled - see Makefile'); diff --git a/spm_get_lm.mexa64 b/spm_get_lm.mexa64 index 545e5006..42954ab6 100755 Binary files a/spm_get_lm.mexa64 and b/spm_get_lm.mexa64 differ diff --git a/spm_get_lm.mexmaci64 b/spm_get_lm.mexmaci64 index e9b00442..56527d3c 100755 Binary files a/spm_get_lm.mexmaci64 and b/spm_get_lm.mexmaci64 differ diff --git a/spm_get_lm.mexw32 b/spm_get_lm.mexw32 index 6df0b6a4..98661b50 100755 Binary files a/spm_get_lm.mexw32 and b/spm_get_lm.mexw32 differ diff --git a/spm_get_lm.mexw64 b/spm_get_lm.mexw64 index fc7d180f..b4a0a791 100755 Binary files a/spm_get_lm.mexw64 and b/spm_get_lm.mexw64 differ diff --git a/spm_global.mexa64 b/spm_global.mexa64 index 201dbf21..9adf933e 100755 Binary files a/spm_global.mexa64 and b/spm_global.mexa64 differ diff --git a/spm_global.mexmaci64 b/spm_global.mexmaci64 index 65ab0a50..001c9c4b 100755 Binary files a/spm_global.mexmaci64 and b/spm_global.mexmaci64 differ diff --git a/spm_global.mexw32 b/spm_global.mexw32 index adb9d2f5..62146586 100755 Binary files a/spm_global.mexw32 and b/spm_global.mexw32 differ diff --git a/spm_global.mexw64 b/spm_global.mexw64 index b80365c8..e920a9c0 100755 Binary files a/spm_global.mexw64 and b/spm_global.mexw64 differ diff --git a/spm_gn_fmin.m b/spm_gn_fmin.m index eb1e79f3..a1a1faaf 100644 --- a/spm_gn_fmin.m +++ b/spm_gn_fmin.m @@ -16,7 +16,7 @@ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_gn_fmin.m 5219 2013-01-29 17:07:07Z spm $ +% $Id: spm_gn_fmin.m 6654 2015-12-22 12:55:36Z spm $ % stochastic search @@ -45,7 +45,7 @@ for i = 1:N F(i) = feval(fun,spm_unvec(p(:,i),Q),varargin{:}); end -[f i] = min(F); +[f,i] = min(F); pmin = p(:,i); clear p F diff --git a/spm_graph.m b/spm_graph.m index 3fe5f537..0d8ca6ea 100644 --- a/spm_graph.m +++ b/spm_graph.m @@ -24,7 +24,7 @@ % Copyright (C) 1996-2013 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_graph.m 6025 2014-05-29 13:35:51Z guillaume $ +% $Id: spm_graph.m 6490 2015-06-26 11:51:46Z guillaume $ if nargin == 3 && isstruct(SPM) && isstruct(XYZ) && ishandle(xG) @@ -39,13 +39,13 @@ %========================================================================== Y = []; try - y = spm_get_data(SPM.xY.VY,XYZ); + y = spm_data_read(SPM.xY.VY,'xyz',XYZ); catch try % remap files in SPM.xY.P if SPM.xY.VY is no longer valid %------------------------------------------------------------------ - SPM.xY.VY = spm_vol(SPM.xY.P); - y = spm_get_data(SPM.xY.VY,XYZ); + SPM.xY.VY = spm_data_hdr_read(SPM.xY.P); + y = spm_data_read(SPM.xY.VY,'xyz',XYZ); catch % data has been moved or renamed @@ -64,15 +64,15 @@ spm('Pointer','Arrow'); return; end - SPM.xY.VY = spm_vol(SPM.xY.P); + SPM.xY.VY = spm_data_hdr_read(SPM.xY.P); for i = 1:numel(SPM.xY.VY) SPM.xY.VY(i).pinfo(1:2,:) = ... SPM.xY.VY(i).pinfo(1:2,:)*SPM.xGX.gSF(i); end - y = spm_get_data(SPM.xY.VY,XYZ); + y = spm_data_read(SPM.xY.VY,'xyz',XYZ); case 'Search' SPM.xY.VY = spm_check_filename(SPM.xY.VY); - y = spm_get_data(SPM.xY.VY,XYZ); + y = spm_data_read(SPM.xY.VY,'xyz',XYZ); otherwise y = []; end @@ -106,15 +106,15 @@ %-Parameter estimates: beta = xX.pKX*xX.K*y; %-Residual mean square: ResMS = sum(R.^2)/xX.trRV %---------------------------------------------------------------------- - beta = spm_get_data(SPM.Vbeta, XYZ); - ResMS = spm_get_data(SPM.VResMS,XYZ); + beta = spm_data_read(SPM.Vbeta,'xyz',XYZ); + ResMS = spm_data_read(SPM.VResMS,'xyz',XYZ); Bcov = ResMS*SPM.xX.Bcov; else % or conditional estimates with % Cov(b|y) through Taylor approximation %---------------------------------------------------------------------- - beta = spm_get_data(SPM.VCbeta, XYZ); + beta = spm_data_read(SPM.VCbeta, 'xyz', XYZ); if isfield(SPM.PPM,'VB') % Get approximate posterior covariance at ic @@ -123,17 +123,17 @@ % Get posterior SD beta's Nk = size(SPM.xX.X,2); for k=1:Nk - sd_beta(k,:) = spm_get_data(SPM.VPsd(k),XYZ); + sd_beta(k,:) = spm_data_read(SPM.VPsd(k),'xyz',XYZ); end % Get AR coefficients nsess = length(SPM.Sess); for ss=1:nsess for p=1:SPM.PPM.AR_P - Sess(ss).a(p,:) = spm_get_data(SPM.PPM.Sess(ss).VAR(p),XYZ); + Sess(ss).a(p,:) = spm_data_read(SPM.PPM.Sess(ss).VAR(p),'xyz',XYZ); end % Get noise SD - Sess(ss).lambda = spm_get_data(SPM.PPM.Sess(ss).VHp,XYZ); + Sess(ss).lambda = spm_data_read(SPM.PPM.Sess(ss).VHp,'xyz',XYZ); end % Which block are we in ? @@ -165,7 +165,7 @@ else Bcov = SPM.PPM.Cby; for j = 1:length(SPM.PPM.l) - l = spm_get_data(SPM.VHp(j),XYZ); + l = spm_data_read(SPM.VHp(j),'xyz',XYZ); Bcov = Bcov + SPM.PPM.dC{j}*(l - SPM.PPM.l(j)); end end diff --git a/spm_hist.mexw32 b/spm_hist.mexw32 index ab363bf9..10e9c84d 100755 Binary files a/spm_hist.mexw32 and b/spm_hist.mexw32 differ diff --git a/spm_hist.mexw64 b/spm_hist.mexw64 index 7630ce89..769f86a8 100755 Binary files a/spm_hist.mexw64 and b/spm_hist.mexw64 differ diff --git a/spm_hist2.mexw32 b/spm_hist2.mexw32 index b742856a..b211b8f8 100755 Binary files a/spm_hist2.mexw32 and b/spm_hist2.mexw32 differ diff --git a/spm_hist2.mexw64 b/spm_hist2.mexw64 index 435561c4..ac752e26 100755 Binary files a/spm_hist2.mexw64 and b/spm_hist2.mexw64 differ diff --git a/spm_hrf.m b/spm_hrf.m index 83f78b35..d22a2a8b 100644 --- a/spm_hrf.m +++ b/spm_hrf.m @@ -1,33 +1,37 @@ function [hrf,p] = spm_hrf(RT,P,T) -% Return a hemodynamic response function +% Haemodynamic response function % FORMAT [hrf,p] = spm_hrf(RT,p,T) % RT - scan repeat time % p - parameters of the response function (two Gamma functions) % % defaults -% (seconds) +% {seconds} % p(1) - delay of response (relative to onset) 6 % p(2) - delay of undershoot (relative to onset) 16 % p(3) - dispersion of response 1 % p(4) - dispersion of undershoot 1 % p(5) - ratio of response to undershoot 6 -% p(6) - onset (seconds) 0 -% p(7) - length of kernel (seconds) 32 +% p(6) - onset {seconds} 0 +% p(7) - length of kernel {seconds} 32 % % T - microtime resolution [Default: 16] % -% hrf - hemodynamic response function +% hrf - haemodynamic response function % p - parameters of the response function %__________________________________________________________________________ -% Copyright (C) 1996-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1996-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_hrf.m 6108 2014-07-16 15:24:06Z guillaume $ +% $Id: spm_hrf.m 6594 2015-11-06 18:47:05Z guillaume $ %-Parameters of the response function %-------------------------------------------------------------------------- -p = [6 16 1 1 6 0 32]; +try + p = spm_get_defaults('stats.fmri.hrf'); +catch + p = [6 16 1 1 6 0 32]; +end if nargin > 1 p(1:length(P)) = P; end @@ -40,7 +44,7 @@ fMRI_T = spm_get_defaults('stats.fmri.t'); end -%-Modelled hemodynamic response function - {mixture of Gammas} +%-Modelled haemodynamic response function - {mixture of Gammas} %-------------------------------------------------------------------------- dt = RT/fMRI_T; u = [0:ceil(p(7)/dt)] - p(6)/dt; diff --git a/spm_input.m b/spm_input.m index 0098ad77..c0b7d6a4 100644 --- a/spm_input.m +++ b/spm_input.m @@ -171,7 +171,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Andrew Holmes -% $Id: spm_input.m 6397 2015-04-02 15:07:46Z guillaume $ +% $Id: spm_input.m 6510 2015-07-31 14:49:33Z guillaume $ %======================================================================= @@ -1278,17 +1278,17 @@ %-Store button # in buttons 'UserData' %-Store handle of prompt string in buttons 'Max' property %-Callback sets UserData of prompt string to button number. - cb = ['set(get(gcbo,''Max''),''UserData'',get(gcbo,''UserData''))']; + cb = ['set(get(gcbo,''UserData''),''UserData'',get(gcbo,''Max''))']; if TTips, str=sprintf('select by mouse or enter value in text widget'); else str=''; end H = []; for i=1:nLabels h = uicontrol(Finter,'Style','Pushbutton',... 'String',deblank(Labels(i,:)),... - 'Max',hPrmpt,... + 'Max',i,... 'ToolTipString',sprintf('%s\n%s',deblank(Labels(i,:)),str),... 'Tag',Tag,... - 'UserData',i,... + 'UserData',hPrmpt,... 'BackgroundColor',COLOUR,... 'Callback',cb,... 'Position',[RRec(1)+(i-1)*dX+1 RRec(2) dX-2 RRec(4)]); @@ -1298,10 +1298,10 @@ %-Default button surrounding edit widget (if a DefStr given) %-Callback sets hPrmpt UserData, and EditWidget string, to DefStr % (Buttons UserData holds handles [hPrmpt,hEditWidget], set later) - cb = ['set(get(gcbo,''UserData'')*[1;0],''UserData'',',... - 'get(gcbo,''String'')),',... - 'set(get(gcbo,''UserData'')*[0;1],''String'',',... - 'get(gcbo,''String''))']; + cb = ['set(subsref(get(gcbo,''UserData''),substruct(''()'',{1})),',... + '''UserData'',get(gcbo,''String'')),',... + 'set(subsref(get(gcbo,''UserData''),substruct(''()'',{2})),',... + '''String'',get(gcbo,''String''))']; if ~isempty(DefStr) hDef = uicontrol(Finter,'Style','PushButton',... 'String',DefStr,... @@ -1405,7 +1405,7 @@ nLabels = size(Labels,1); for i = 1:nLabels, fprintf('\t%2d : %s\n',i,Labels(i,:)), end Prmpt = ['Menu choice (1-',int2str(nLabels),')']; - if DefItem + if ~isempty(DefItem) Prmpt=[Prmpt,' (Default: ',num2str(DefItem),')']; end @@ -1417,11 +1417,11 @@ fprintf('Menu choice: 1 - %s\t(only option)',Labels) else k = input([Prmpt,' ? ']); - if DefItem && isempty(k), k=DefItem; end + if ~isempty(DefItem) && isempty(k), k=DefItem; end while isempty(k) || ~any([1:nLabels]==k) if ~isempty(k),fprintf('%c\t!Out of range\n',7),end k = input([Prmpt,' ? ']); - if DefItem && isempty(k), k=DefItem; end + if ~isempty(DefItem) && isempty(k), k=DefItem; end end end fprintf('\n') @@ -1436,7 +1436,7 @@ else Labs=[repmat(' ',nLabels,2),Labels]; - if DefItem + if ~isempty(DefItem) Labs(DefItem,1)='*'; H = uicontrol(Finter,'Style','Frame',... 'BackGroundColor','k',... diff --git a/spm_jobman.m b/spm_jobman.m index 428cd7a8..ee957556 100644 --- a/spm_jobman.m +++ b/spm_jobman.m @@ -9,7 +9,7 @@ % FORMAT output_list = spm_jobman('run',job[,input1,...inputN]) % FORMAT [output_list, hjob] = spm_jobman('run',job[,input1,...inputN]) % Run specified job. -% job - filename of a job (.m, .mat or .xml), or +% job - filename of a job (.m or .mat), or % cell array of filenames, or % 'jobs'/'matlabbatch' variable, or % cell array of 'jobs'/'matlabbatch' variables. @@ -38,7 +38,7 @@ % job_id = spm_jobman('interactive',job[,node]) % job_id = spm_jobman('interactive','',node) % Run the user interface in interactive mode. -% job - filename of a job (.m, .mat or .xml), or +% job - filename of a job (.m or .mat), or % cell array of filenames, or % 'jobs'/'matlabbatch' variable, or % cell array of 'jobs'/'matlabbatch' variables. @@ -56,7 +56,7 @@ % Copyright (C) 2005-2015 Wellcome Trust Centre for Neuroimaging % Volkmar Glauche -% $Id: spm_jobman.m 6423 2015-04-23 18:38:01Z guillaume $ +% $Id: spm_jobman.m 6656 2015-12-24 16:49:52Z guillaume $ %__________________________________________________________________________ @@ -103,9 +103,10 @@ %-------------------------------------------------------------------------- persistent isInitCfg; if isempty(isInitCfg) && ~(nargin == 1 && strcmpi(varargin{1},'initcfg')) - warning('spm:spm_jobman:notInitialised',... - 'Run spm_jobman(''initcfg''); beforehand'); + % Run spm_jobman('initcfg') beforehand. + fprintf('Initialising batch system... '); spm_jobman('initcfg'); + fprintf('done.\n'); end isInitCfg = true; @@ -303,10 +304,8 @@ % function newjobs = load_jobs(job) %========================================================================== function newjobs = load_jobs(job) -% Load a list of possible job files, return a cell list of jobs. Jobs can -% be either SPM5 (i.e. containing a 'jobs' variable) or matlabbatch -% jobs. If a job file failed to load, an empty cell is returned in the -% list. +% Load a list of possible job files, return a cell list of jobs. +% If a job file failed to load, an empty cell is returned in the list. filenames = cellstr(job); newjobs = {}; for i = 1:numel(filenames) @@ -331,18 +330,6 @@ catch warning('spm:spm_jobman:loadFailed','Load failed: ''%s''',filenames{i}); end - case 'xml' - spm('Pointer','Watch'); - try - loadxml(filenames{i},'jobs'); - catch - try - loadxml(filenames{i},'matlabbatch'); - catch - warning('spm:spm_jobman:loadFailed','Load failed: ''%s''',filenames{i}); - end - end - spm('Pointer','Arrow'); otherwise warning('spm:spm_jobman:unknownExt','Unknown extension: ''%s''',filenames{i}); end diff --git a/spm_jsonread.m b/spm_jsonread.m new file mode 100644 index 00000000..99102900 --- /dev/null +++ b/spm_jsonread.m @@ -0,0 +1,18 @@ +function json = spm_jsonread(filename) +% JSON (JavaScript Object Notation) parser - a compiled routine +% FORMAT json = spm_jsonread(filename) +% filename - name of a JSON file or JSON string +% json - JSON structure +% +% References: +% http://www.json.org/ +% http://zserge.com/jsmn.html +%_______________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: spm_jsonread.m 6589 2015-11-03 16:01:08Z guillaume $ + + +%-This is merely the help file for the compiled routine +error('spm_jsonread.c not compiled - see Makefile') diff --git a/spm_jsonread.mexa64 b/spm_jsonread.mexa64 new file mode 100755 index 00000000..50d02f1a Binary files /dev/null and b/spm_jsonread.mexa64 differ diff --git a/spm_jsonread.mexmaci64 b/spm_jsonread.mexmaci64 new file mode 100755 index 00000000..bdaf1f9c Binary files /dev/null and b/spm_jsonread.mexmaci64 differ diff --git a/spm_jsonread.mexw32 b/spm_jsonread.mexw32 new file mode 100755 index 00000000..1cb098f2 Binary files /dev/null and b/spm_jsonread.mexw32 differ diff --git a/spm_jsonread.mexw64 b/spm_jsonread.mexw64 new file mode 100755 index 00000000..16fc0d1d Binary files /dev/null and b/spm_jsonread.mexw64 differ diff --git a/spm_jsonwrite.m b/spm_jsonwrite.m new file mode 100644 index 00000000..e7435bb9 --- /dev/null +++ b/spm_jsonwrite.m @@ -0,0 +1,120 @@ +function varargout = spm_jsonwrite(varargin) +% Serialize a JSON (JavaScript Object Notation) structure +% FORMAT spm_jsonwrite(filename,json) +% filename - JSON filename +% json - JSON structure +% +% FORMAT S = spm_jsonwrite(json) +% json - JSON structure +% S - serialized JSON structure (string) +% +% References: +% http://www.json.org/ +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: spm_jsonwrite.m 6610 2015-11-25 17:50:05Z guillaume $ + + +%-Input parameters +%-------------------------------------------------------------------------- +if nargin > 1 + filename = varargin{1}; + json = varargin{2}; + root = inputname(2); +else + filename = ''; + json = varargin{1}; + root = inputname(1); +end + +%-JSON serialization +%-------------------------------------------------------------------------- +if ~isstruct(json) && ~iscell(json) + if ~isempty(root) + json = struct(root,json); + else + error('Invalid JSON structure.'); + end +end +S = jsonwrite_var(json); + +%-Output +%-------------------------------------------------------------------------- +if isempty(filename) + varargout = { S }; +else + fid = fopen(filename,'wt'); + if fid == -1 + error('Unable to open file "%s" for writing.',filename); + end + fprintf(fid,'%s',S); + fclose(fid); +end + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function S = jsonwrite_var(json,tab) +if nargin < 2, tab = 0; end +if isstruct(json) + S = jsonwrite_struct(json,tab); +elseif iscell(json) + S = jsonwrite_cell(json,tab); +elseif ischar(json) + S = jsonwrite_char(json); +elseif isnumeric(json) || islogical(json) + S = jsonwrite_numeric(json); +else + error('Class "%s" is not supported.',class(json)); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function S = jsonwrite_struct(json,tab) +if numel(json) == 1 + fn = fieldnames(json); + S = ['{' sprintf('\n')]; + for i=1:numel(fn) + S = [S blanks((tab+1)*2) jsonwrite_char(fn{i}) ': ' ... + jsonwrite_var(json.(fn{i}),tab+1)]; + if i ~= numel(fn), S = [S ',']; end + S = [S sprintf('\n')]; + end + S = [S blanks(2*tab) '}']; +else + S = jsonwrite_cell(arrayfun(@(x) {x},json),tab); +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function S = jsonwrite_cell(json,tab) +S = ['[' sprintf('\n')]; +for i=1:numel(json) + S = [S blanks((tab+1)*2) jsonwrite_var(json{i},tab+1)]; + if i ~= numel(json), S = [S ',']; end + S = [S sprintf('\n')]; +end +S = [S blanks(2*tab) ']']; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function S = jsonwrite_char(json) +% any-Unicode-character-except-"-or-\-or-control-character +% \" \\ \/ \b \f \n \r \t \u four-hex-digits +json = regexprep(json,'[^\\]"','\\"'); +S = ['"' json '"']; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +function S = jsonwrite_numeric(json) +if numel(json) > 1 + warning('Not supported: converting to JSON array.'); + S = jsonwrite_cell(num2cell(json),0); % consider array of array? + return; +end +if islogical(json) + if json, S = 'true'; else S = 'false'; end +else + if isnan(json) + S = 'null'; + else + S = num2str(json); + end +end diff --git a/spm_krutil.mexw32 b/spm_krutil.mexw32 index b136d556..6dd70e37 100755 Binary files a/spm_krutil.mexw32 and b/spm_krutil.mexw32 differ diff --git a/spm_krutil.mexw64 b/spm_krutil.mexw64 index 94f59b66..af55a97c 100755 Binary files a/spm_krutil.mexw64 and b/spm_krutil.mexw64 differ diff --git a/spm_list.m b/spm_list.m index 038c7855..150c3c94 100644 --- a/spm_list.m +++ b/spm_list.m @@ -114,7 +114,7 @@ % Copyright (C) 1999-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston, Andrew Holmes, Guillaume Flandin -% $Id: spm_list.m 6425 2015-04-29 18:24:51Z guillaume $ +% $Id: spm_list.m 6619 2015-12-01 19:20:22Z guillaume $ %========================================================================== @@ -318,9 +318,12 @@ TabDat.ftr{4,1} = ... 'Expected number of clusters, = %0.2f'; TabDat.ftr{4,2} = Ec*Pn; - if any(isnan(uc)) + if isnan(uc(3)) TabDat.ftr{5,1} = 'FWEp: %0.3f, FDRp: %0.3f'; TabDat.ftr{5,2} = uc(1:2); + elseif isnan(uc(4)) + TabDat.ftr{5,1} = 'FWEp: %0.3f, FDRp: %0.3f, FWEc: %0.0f'; + TabDat.ftr{5,2} = uc(1:3); else TabDat.ftr{5,1} = ... 'FWEp: %0.3f, FDRp: %0.3f, FWEc: %0.0f, FDRc: %0.0f'; @@ -801,6 +804,12 @@ if nargin < 3, hReg = []; else hReg = varargin{3}; end xSPM = varargin{2}; + if isfield(xSPM,'G') + warning('"current cluster" option not implemented for meshes.'); + varargout = { evalin('base','TabDat') }; + return; + end + %-Get number of maxima per cluster to be reported %------------------------------------------------------------------ if nargin < 4, Num = spm_get_defaults('stats.results.svc.nbmax'); diff --git a/spm_load.m b/spm_load.m index abfb9cb5..f59df95b 100644 --- a/spm_load.m +++ b/spm_load.m @@ -1,46 +1,185 @@ -function [x] = spm_load(f) -% function to load ascii file data as matrix -% FORMAT [x] = spm_load(f) -% f - file {ascii file containing a regular array of numbers -% x - corresponding data matrix -%_______________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging - -% Karl Friston -% $Id: spm_load.m 1143 2008-02-07 19:33:33Z spm $ +function x = spm_load(f,v) +% Load text and numeric data from file +% FORMAT x = spm_load(f,v) +% f - filename {txt,mat,csv,tsv,json} +% v - name of field to return if data stored in a structure [default: ''] +% or index of column if data stored as an array +% +% x - corresponding data array or structure +%__________________________________________________________________________ +% Copyright (C) 1995-2015 Wellcome Trust Centre for Neuroimaging +% Guillaume Flandin +% $Id: spm_load.m 6646 2015-12-14 19:00:26Z guillaume $ %-Get a filename if none was passed -%----------------------------------------------------------------------- -x = []; -if nargin == 0 - [f,p] = uigetfile({'*.mat';'*.txt';'*.dat'}); - try - f = fullfile(p,f); - end +%-------------------------------------------------------------------------- +if ~nargin + [f,sts] = spm_select(1,{... + 'mat',... % *.txt, *.mat + '^.*\.csv$','^.*\.csv.gz$',... % *.csv, *.csv.gz + '^.*\.tsv$','^.*\.tsv.gz$',... % *.tsv, *.tsv.gz + '^.*\.json$','^.*\.json.gz$',... % *.json, *.json.gz + }); + if ~sts, x = []; return; end end -%-Load the data file into double precision matrix x -%----------------------------------------------------------------------- -try - x = load(f,'-ascii'); - return +if ~exist(f,'file') + error('Unable to read file ''%s''',f); +end + +if nargin < 2, v = ''; end + +%-Load the data file +%-------------------------------------------------------------------------- +switch spm_file(f,'ext') + case 'txt' + x = load(f,'-ascii'); + case 'mat' + x = load(f,'-mat'); + case 'csv' + try + x = csvread(f); + catch + x = dsvread(f,','); + end + case 'tsv' + try + x = dlmread(f,'\t'); + catch + x = dsvread(f,'\t'); + end + case 'json' + x = spm_jsonread(f); + case 'gz' + fz = gunzip(f,tempname); + sts = true; + try + x = spm_load(fz{1}); + catch + sts = false; + end + delete(fz{1}); + rmdir(spm_file(fz{1},'path')); + if ~sts, error('Cannot load ''%s''.',f); end + otherwise + try + x = load(f); + catch + error('Unknown file format.'); + end end -try - x = load(f,'-mat'); - x = getdata(x); + +%-Return relevant subset of the data if required +%-------------------------------------------------------------------------- +if isstruct(x) + if isempty(v) + fn = fieldnames(x); + if numel(fn) == 1 && isnumeric(x.(fn{1})) + x = x.(fn{1}); + end + else + if ischar(v) + try + x = x.(v); + catch + error('Data do not contain array ''%s''.',v); + end + else + fn = fieldnames(x); + try + x = x.(fn{v}); + catch + error('Invalid data index.'); + end + end + end +elseif isnumeric(x) + if isnumeric(v) + try + x = x(:,v); + catch + error('Invalid data index.'); + end + elseif ~isempty(v) + error('Invalid data index.'); + end end -if ~isnumeric(x), x = []; end -function x = getdata(s) -% get numberic data x from the fields of structure s +%========================================================================== +% function x = dsvread(f,delim) +%========================================================================== +function x = dsvread(f,delim) +% Read delimiter-separated values file into a structure array +% * header line of column names will be used if detected +% * 'n/a' fields are replaced with NaN + +%-Input arguments %-------------------------------------------------------------------------- -x = []; -f = fieldnames(s); -for i = 1:length(f) - x = s.(f{i}); - if isnumeric(x),return; end - if isstruct(x), x = getdata(x); end +if nargin < 2, delim = '\t'; end +delim = sprintf(delim); +eol = sprintf('\n'); + +%-Read file +%-------------------------------------------------------------------------- +S = fileread(f); +if isempty(S), x = []; return; end +if S(end) ~= eol, S = [S eol]; end +S = regexprep(S,{'\r\n','(\n)\1+'},{'\n','$1'}); + +%-Get column names from header line (non-numeric first line) +%-------------------------------------------------------------------------- +h = find(S == eol,1); +hdr = S(1:h-1); +var = regexp(hdr,delim,'split'); +N = numel(var); +n1 = isnan(cellfun(@str2double,var)); +n2 = cellfun(@(x) strcmpi(x,'NaN'),var); +if any(n1 & ~n2) + hdr = true; + try + var = genvarname(var); + catch + var = matlab.lang.makeValidName(var); + var = matlab.lang.makeUniqueStrings(var); + end + S = S(h+1:end); +else + hdr = false; + var = cell(N,1); + for i=1:N + var{i} = sprintf(['var%0' num2str(floor(log10(N))+1) 'd'],i); + end +end + +%-Parse file +%-------------------------------------------------------------------------- +d = textscan(S,'%s','Delimiter',delim); +if rem(numel(d{1}),N), error('Varying number of delimiters per line.'); end +d = reshape(d{1},N,[])'; +allnum = true; +for i=1:numel(var) + sts = true; + dd = zeros(size(d,1),1); + for j=1:size(d,1) + if strcmp(d{j,i},'n/a') + dd(j) = NaN; + else + dd(j) = str2double(d{j,i}); % i,j considered as complex + if isnan(dd(j)), sts = false; break; end + end + end + if sts + x.(var{i}) = dd; + else + x.(var{i}) = d(:,i); + allnum = false; + end +end + +if ~hdr && allnum + x = struct2cell(x); + x = [x{:}]; end diff --git a/spm_log_evidence_reduce.m b/spm_log_evidence_reduce.m index 28a843a5..e480f803 100644 --- a/spm_log_evidence_reduce.m +++ b/spm_log_evidence_reduce.m @@ -24,7 +24,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_log_evidence_reduce.m 6449 2015-05-24 14:26:59Z karl $ +% $Id: spm_log_evidence_reduce.m 6481 2015-06-16 17:01:47Z karl $ % Compute reduced log-evidence @@ -42,7 +42,7 @@ % Remove (a priori) null space %-------------------------------------------------------------------------- E = rE; -U = spm_svd(pC,1e-4); +U = spm_svd(pC,1e-6); qE = U'*spm_vec(qE); pE = U'*spm_vec(pE); rE = U'*spm_vec(rE); diff --git a/spm_mesh_calc.m b/spm_mesh_calc.m new file mode 100644 index 00000000..151b8f35 --- /dev/null +++ b/spm_mesh_calc.m @@ -0,0 +1,100 @@ +function Mo = spm_mesh_calc(Mi,Mo,f,varargin) +% Evaluate a function on a mesh's data +% FORMAT Mo = spm_mesh_calc(Mi,Mo,f,opts) +% Mi - input filenames (char array or cellstr) +% or cell array of gifti objects or patch structures +% Mo - output filename +% if empty, a gifti object is returned and not saved on disk +% f - MATLAB expression to be evaluated (string or function handle) +% (e.g., f = '(s1.*s2).^2' or f = @(s1,s2) (s1.*s2).^2) +% opts - optional list of pairs of property names and values +% dmtx - read images into data matrix X [default: false] +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Guillaume Flandin +% $Id: spm_mesh_calc.m 6656 2015-12-24 16:49:52Z guillaume $ + + +%-Check input arguments +%-------------------------------------------------------------------------- +if ~isempty(Mo) + Mo = spm_file(Mo,'ext','.gii'); + Mo = spm_file(Mo,'cpath'); +end +if numel(varargin) == 1 && isstruct(varargin{1}) + varargin = reshape([fieldnames(varargin{1})';struct2cell(varargin{1})'],1,[]); +end +if mod(numel(varargin),2) + error('Incorrect number of input arguments.'); +end +opts.dmtx = false; +for i=1:2:numel(varargin) + switch lower(varargin{i}) + case 'dmtx' + opts.dmtx = logical(varargin{i+1}); + otherwise + error('Unknown option %s.',varargin{i}); + end +end + +%-Load data +%-------------------------------------------------------------------------- +if ischar(Mi), Mi = cellstr(Mi); end +for i=1:numel(Mi) + g = gifti(Mi{i}); + if ~isfield(g,'cdata') + error('File %s does not contain data.',Mi{i}); + end + D = g.cdata(); + if i==1 + nv = size(D); + else + if ~isequal(size(D),nv) + error('Data dimension mismatch.'); + end + end + D = reshape(D,1,[]); + if opts.dmtx + X(i,:) = D; + else + eval(['s',num2str(i),'=D;']); + end +end + +%-Evaluate function +%-------------------------------------------------------------------------- +if ischar(f) + try + eval(['S = ' f ';']); + catch + l = lasterror; + error('%s\nCan''t evaluate "%s".',l.message,f); + end +elseif isa(f,'function_handle') + try + if opts.dmtx + S = feval(f,X); + else + list = sprintf('s%d,',1:numel(Mi)); list = list(1:end-1); + eval(['S = feval(f,' list ');']); + end + catch + l = lasterror; + error('%s\nCan''t evaluate "%s".',l.message,func2str(f)); + end +else + error('Unknown function input.'); +end + +%-Return or save output +%-------------------------------------------------------------------------- +gs = gifti(reshape(S,nv)); +g = gifti(Mi{i}); +if isfield(g,'vertices') && isfield(g,'faces') + gs.vertices = g.vertices; + gs.faces = g.faces; +elseif ~isempty(g.private.metadata) + gs.private.metadata(1) = g.private.metadata(1); +end +if isempty(Mo), Mo = gs; else save(gs,Mo,'ExternalFileBinary'); end diff --git a/spm_mesh_inflate.m b/spm_mesh_inflate.m index e325843c..9e6b63cb 100644 --- a/spm_mesh_inflate.m +++ b/spm_mesh_inflate.m @@ -10,7 +10,7 @@ % Copyright (C) 2009-2011 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin & Jean Daunizeau -% $Id: spm_mesh_inflate.m 6157 2014-09-05 18:17:54Z guillaume $ +% $Id: spm_mesh_inflate.m 6535 2015-08-25 11:45:26Z vladimir $ if nargin < 3, S = 0; end @@ -40,6 +40,7 @@ if nargin < 2 || isinf(T) T = floor(size(v,1) * 0.003 - 2); + T = max(T,1); end % Compute (normalised) adjacency matrix diff --git a/spm_mesh_render.m b/spm_mesh_render.m index f9eb7972..feee0ba9 100644 --- a/spm_mesh_render.m +++ b/spm_mesh_render.m @@ -35,7 +35,7 @@ % Copyright (C) 2010-2011 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_mesh_render.m 5411 2013-04-15 11:45:08Z guillaume $ +% $Id: spm_mesh_render.m 6637 2015-12-08 18:12:51Z guillaume $ %-Input parameters @@ -169,6 +169,9 @@ uimenu(cmenu, 'Label','Image Sections...', 'Interruptible','off', ... 'Callback',{@myImageSections, H}); + uimenu(cmenu, 'Label','Change geometry...', 'Interruptible','off', ... + 'Callback',{@myChangeGeometry, H}); + c = uimenu(cmenu, 'Label', 'Connected Components', 'Interruptible','off'); C = getappdata(H.patch,'cclabel'); for i=1:length(unique(C)) @@ -575,7 +578,8 @@ function mySave(obj,evt,H) '*.gii' 'GIfTI files (*.gii)'; ... '*.png' 'PNG files (*.png)';... '*.dae' 'Collada files (*.dae)';... - '*.idtf' 'IDTF files (*.idtf)'}, 'Save as'); + '*.idtf' 'IDTF files (*.idtf)';... + '*.vtk' 'VTK files (*.vtk)'}, 'Save as'); if ~isequal(filename,0) && ~isequal(pathname,0) [pth,nam,ext] = fileparts(filename); switch ext @@ -587,6 +591,8 @@ function mySave(obj,evt,H) filterindex = 3; case '.idtf' filterindex = 4; + case {'.vtk','.vtp'} + filterindex = 5; otherwise switch filterindex case 1 @@ -595,6 +601,10 @@ function mySave(obj,evt,H) filename = [filename '.png']; case 3 filename = [filename '.dae']; + case 4 + filename = [filename '.idtf']; + case 5 + filename = [filename '.vtk']; end end switch filterindex @@ -642,9 +652,11 @@ function mySave(obj,evt,H) close(h); set(getappdata(obj,'fig'),'renderer',r); case 3 - save(gifti(H.patch),fullfile(pathname, filename),'collada'); + saveas(gifti(H.patch),fullfile(pathname, filename),'collada'); case 4 - save(gifti(H.patch),fullfile(pathname, filename),'idtf'); + saveas(gifti(H.patch),fullfile(pathname, filename),'idtf'); + case 5 + saveas(gifti(H.patch),fullfile(pathname, filename),'vtk'); end end @@ -665,6 +677,17 @@ function myImageSections(obj,evt,H) if ~sts, return; end renderSlices(H,P); +%========================================================================== +function myChangeGeometry(obj,evt,H) +[P, sts] = spm_select(1,'mesh','Select new geometry mesh'); +if ~sts, return; end +G = gifti(P); +if size(H.patch.Vertices,1) ~= size(G.vertices,1) + error('Number of vertices must match.'); +end +H.patch.Vertices = G.vertices; +H.patch.Faces = G.faces; + %========================================================================== function renderSlices(H,P,pls) if nargin <3 diff --git a/spm_mesh_resels.m b/spm_mesh_resels.m index b3492317..c63ba021 100644 --- a/spm_mesh_resels.m +++ b/spm_mesh_resels.m @@ -1,9 +1,10 @@ -function [R, RPV] = spm_mesh_resels(M,T,S) +function [R, RPV] = spm_mesh_resels(M,T,S,ndf) % Returns the RESEL counts of a search volume on a surface mesh % FORMAT R = spm_mesh_resels(M,T,[S]) % M - a patch structure or [nx3] faces array (#faces = n) % T - a [mx1] logical vector (#vertices = m) defining search volume % S - a [mxp] array of standardised residuals [optional] +% ndf - a 2-vector, [n df], the original n & dof of the linear model % % R - a [1xD] array of RESEL counts {adimensional} % RPV - a [mx1] vector of RESELs per vertex @@ -17,20 +18,23 @@ % % [2] SurfStat: http://www.math.mcgill.ca/keith/surfstat/, K.J. Worsley. %__________________________________________________________________________ -% Copyright (C) 2010-2012 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2010-2016 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_mesh_resels.m 5097 2012-12-06 16:08:16Z guillaume $ +% $Id: spm_mesh_resels.m 6678 2016-01-14 18:23:33Z guillaume $ %-Parse input arguments %-------------------------------------------------------------------------- -if nargin < 3 +if nargin < 3 || isempty(S) if ~isnumeric(M) S = M.vertices; else S = ones(max(M(:)),2); end end +if nargin < 4 + ndf = [size(S,2) size(S,2)]; % Assume full df +end if ~isnumeric(M), M = M.faces; end @@ -51,6 +55,7 @@ SSR = S(E',:); SSR = reshape(SSR',size(SSR,2),2,[]); SSR = mean(squeeze((SSR(:,1,:) - SSR(:,2,:)).^2),1)'; +SSR = SSR * (ndf(1)/ndf(2)); % see comment in spm_est_smoothness.m SSR = sqrt(SSR); %-Lipschitz-Killing Curvature (LKC) diff --git a/spm_mesh_utils.mexw32 b/spm_mesh_utils.mexw32 index 3504b061..c70edbde 100755 Binary files a/spm_mesh_utils.mexw32 and b/spm_mesh_utils.mexw32 differ diff --git a/spm_mesh_utils.mexw64 b/spm_mesh_utils.mexw64 index ef0f16cf..1df4a938 100755 Binary files a/spm_mesh_utils.mexw64 and b/spm_mesh_utils.mexw64 differ diff --git a/spm_mrf.mexw32 b/spm_mrf.mexw32 index eb112e91..fbe244f0 100755 Binary files a/spm_mrf.mexw32 and b/spm_mrf.mexw32 differ diff --git a/spm_mrf.mexw64 b/spm_mrf.mexw64 index 55754e94..51d146e8 100755 Binary files a/spm_mrf.mexw64 and b/spm_mrf.mexw64 differ diff --git a/spm_nlsi_GN.m b/spm_nlsi_GN.m index efeb8388..b1d3a242 100644 --- a/spm_nlsi_GN.m +++ b/spm_nlsi_GN.m @@ -97,7 +97,7 @@ % Copyright (C) 2001-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_nlsi_GN.m 6432 2015-05-09 12:58:12Z karl $ +% $Id: spm_nlsi_GN.m 6569 2015-10-14 08:53:24Z karl $ % options %-------------------------------------------------------------------------- @@ -164,8 +164,6 @@ if isfield(M,'h'), M.h = spm_funcheck(M.h); end - - % size of data (samples x response component x response component ...) %-------------------------------------------------------------------------- if iscell(y) @@ -309,7 +307,6 @@ % time %---------------------------------------------------------------------- tStart = tic; - revert = false; % E-Step: prediction f, and gradients; dfdp %====================================================================== diff --git a/spm_nlsi_Newton.m b/spm_nlsi_Newton.m new file mode 100644 index 00000000..953bb5fb --- /dev/null +++ b/spm_nlsi_Newton.m @@ -0,0 +1,296 @@ +function [Ep,Cp,F] = spm_nlsi_Newton(M,U,Y) +% Variational Lapalce for nonlinear models - Newton's method +% FORMAT [Ep,Cp,F] = spm_nlsi_Newton(M,U,Y) +% +% Eplicit log-likihood model +%__________________________________________________________________________ +% +% M.L - log likelihood function @(P,M,U,Y) +% P - free parameters +% M - model +% +% M.P - starting estimates for model parameters [optional] +% M.pE - prior expectation - E{P} of model parameters +% M.pC - prior covariance - Cov{P} of model parameters +% +% U - inputs or causes +% Y - output or response +% +% Parameter estimates +%-------------------------------------------------------------------------- +% Ep - (p x 1) conditional expectation E{P|y} +% Cp - (p x p) conditional covariance Cov{P|y} +% +% log evidence +%-------------------------------------------------------------------------- +% F - [-ve] free energy F = log evidence = p(Y|pE,pC) = p(y|m) +% +%__________________________________________________________________________ +% Returns the moments of the posterior p.d.f. of the parameters of a +% nonlinear model with a log likelihood function L(P,M,U,Y). +% +% Priors on the free parameters P are specified in terms of expectation pE +% and covariance pC. This Variational Laplace scheme uses an explicit +% (numerical) curvature to implement a gradient ascent on variational free +% energy using Newton's method. An example of its application is provided at +% the end of this routine using a simple general linear model. This example +% eschews the mean field approximation aassociated with standard +% inversions. +% +% For generic aspects of the scheme see: +% +% Friston K, Mattout J, Trujillo-Barreto N, Ashburner J, Penny W. +% Variational free energy and the Laplace approximation. +% NeuroImage. 2007 Jan 1;34(1):220-34. +%__________________________________________________________________________ +% Copyright (C) 2001-2015 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_nlsi_Newton.m 6587 2015-11-02 10:29:49Z karl $ + +% options +%-------------------------------------------------------------------------- +try, M.nograph; catch, M.nograph = 0; end +try, M.noprint; catch, M.noprint = 0; end +try, M.Nmax; catch, M.Nmax = 128; end + +% converted to function handle +%-------------------------------------------------------------------------- +L = spm_funcheck(M.L); + +% initial parameters +%-------------------------------------------------------------------------- +try + M.P; fprintf('\nParameter initialisation successful\n') +catch + M.P = M.pE; +end + +% prior moments (assume uninformative priors if not specifed) +%-------------------------------------------------------------------------- +pE = M.pE; +try + pC = M.pC; +catch + np = spm_length(M.pE); + pC = speye(np,np)*exp(16); +end + +% unpack covariance +%-------------------------------------------------------------------------- +if isstruct(pC); + pC = spm_diag(spm_vec(pC)); +end + +% dimension reduction of parameter space +%-------------------------------------------------------------------------- +V = spm_svd(pC,0); + +% second-order moments (in reduced space) +%-------------------------------------------------------------------------- +pC = V'*pC*V; +ipC = inv(pC); + +% initialize conditional density +%-------------------------------------------------------------------------- +p = V'*(spm_vec(M.P) - spm_vec(M.pE)); +Ep = spm_unvec(spm_vec(pE) + V*p,pE); + +% figure (unless disabled) +%-------------------------------------------------------------------------- +if ~M.nograph, Fsi = spm_figure('GetWin','SI'); clf, end + + +% Wariational Laplace +%========================================================================== +criterion = [0 0 0 0]; +C.F = -Inf; % free energy +v = -4; % log ascent rate +for k = 1:M.Nmax + + % time + %---------------------------------------------------------------------- + tStart = tic; + + % Log-likelihood f, gradients; dfdp and curvature dfdpp + %====================================================================== + [dfdpp,dfdp,f] = spm_diff(L,Ep,M,U,Y,[1 1],{V}); + dfdp = dfdp'; + dfdpp = full(spm_cat(dfdpp')); + + % enure prior bounds on curvature + %---------------------------------------------------------------------- + [E,D] = eig(dfdpp); + D = diag(D); + dfdpp = E*diag(D.*(D < 0))*E'; + + % condiitonal covariance + %---------------------------------------------------------------------- + Cp = inv(ipC - dfdpp); + + % Fre energy: F(p) = log evidence - divergence + %====================================================================== + F = f - p'*ipC*p/2 + spm_logdet(ipC*Cp)/2; + G(k) = F; + + % record increases and reference log-evidence for reporting + %---------------------------------------------------------------------- + if k > 1 + if ~M.noprint + fprintf(' actual: %.3e (%.2f sec)\n',full(F - C.F),toc(tStart)) + end + else + F0 = F; + end + + % if F has increased, update gradients and curvatures for E-Step + %---------------------------------------------------------------------- + if F > C.F || k < 8 + + % accept current estimates + %------------------------------------------------------------------ + C.p = p; + C.F = F; + C.Cp = Cp; + + % E-Step: Conditional update of gradients and curvature + %------------------------------------------------------------------ + dFdp = dfdp - ipC*p; + dFdpp = dfdpp - ipC; + + % decrease regularization + %------------------------------------------------------------------ + v = min(v + 1/2,4); + str = 'EM:(+)'; + + else + + % reset expansion point + %------------------------------------------------------------------ + p = C.p; + + % and increase regularization + %------------------------------------------------------------------ + v = min(v - 2,-4); + str = 'EM:(-)'; + + end + + % E-Step: update + %====================================================================== + dp = spm_dx(dFdpp,dFdp,{v}); + p = p + dp; + Ep = spm_unvec(spm_vec(pE) + V*p,pE); + + % Graphics + %====================================================================== + if exist('Fsi', 'var') + + spm_figure('Select', Fsi) + + % trajectory in parameter space + %------------------------------------------------------------------ + subplot(2,2,1) + plot(0,0,'r.','MarkerSize',32), hold on + col = [exp(-k/4) exp(-k) 1]; + try + plot(V(1,:)*p,V(2,:)*p,'.','MarkerSize',32,'Color',col), hold on + xlabel('1st parameter') + ylabel('2nd parameter') + catch + plot(k,V(1,:)*p,'.','MarkerSize',32,'Color',col), hold on + xlabel('Iteration') + ylabel('1st parameter') + end + title('Trajectory','FontSize',16) + grid on, axis square + + % trajectory in parameter space + %------------------------------------------------------------------ + subplot(2,2,2) + bar(full(G - F0),'c') + xlabel('Iteration') + ylabel('Log-evidence') + title('Free energy','FontSize',16) + grid on, axis square + + % subplot parameters + %-------------------------------------------------------------- + subplot(2,2,3) + bar(full(spm_vec(pE) + V*p)) + xlabel('Parameter') + tstr = 'Conditional expectation'; + title(tstr,'FontSize',16) + grid on, axis square + + % subplot parameters (eigenmodes) + %------------------------------------------------------------------ + subplot(2,2,4) + spm_plot_ci(p,Cp) + xlabel('Parameter (eigenmodes)') + title('Posterior deviations','FontSize',16) + grid on, axis square + drawnow + + end + + % convergence + %---------------------------------------------------------------------- + dF = dFdp'*dp; + if ~M.noprint + fprintf('%-6s: %i %6s %-6.3e %6s %.3e ',str,k,'F:',full(C.F - F0),'dF predicted:',full(dF)) + end + criterion = [(dF < 1e-1) criterion(1:end - 1)]; + if all(criterion) + if ~M.noprint + fprintf(' convergence\n') + end + break + end + +end + +if exist('Fsi', 'var') + spm_figure('Focus', Fsi) +end + +% outputs +%-------------------------------------------------------------------------- +Ep = spm_unvec(spm_vec(pE) + V*C.p,pE); +Cp = V*C.Cp*V'; +F = C.F; + + +return + + +% NB: notes - illustrative application (a simple linear model) +%========================================================================== + +% parameters P and design matrix U +%-------------------------------------------------------------------------- +U = randn(32,2); % design matrix +P.beta = [4;2]; % parameters of GLM +P.pi = 2; % log precision + +% generate data +%-------------------------------------------------------------------------- +Y = U*P.beta + exp(-P.pi/2)*randn(32,1); + +% model specification with log-likelihood function M.L +%-------------------------------------------------------------------------- +M.L = @(P,M,U,Y) sum(log( spm_Npdf(Y, U*P.beta, exp(-P.pi)) )); +M.pE = spm_zeros(P); % prior means (parameters) +M.pC = eye(spm_length(P)); % prior variance (parameters) + +% Variational Laplace +%-------------------------------------------------------------------------- +[Ep,Cp,F] = spm_nlsi_Newton(M,U,Y); + +% overlay true values on confidence intervals +%-------------------------------------------------------------------------- +subplot(2,2,4),hold on +bar(spm_vec(P),1/4) + + + diff --git a/spm_orthviews.m b/spm_orthviews.m index 28846d01..89fe06eb 100644 --- a/spm_orthviews.m +++ b/spm_orthviews.m @@ -151,7 +151,7 @@ % Copyright (C) 1996-2015 Wellcome Trust Centre for Neuroimaging % John Ashburner et al -% $Id: spm_orthviews.m 6371 2015-03-10 20:19:46Z guillaume $ +% $Id: spm_orthviews.m 6656 2015-12-24 16:49:52Z guillaume $ % The basic fields of st are: @@ -278,6 +278,8 @@ mmcentre = mean(st.Space*[maxbb';1 1],2)'; st.centre = mmcentre(1:3); redraw_all + try, set(F, 'WindowScrollWheelFcn', @scroll_wheel); end + case 'caption' if ~isnumeric(varargin{1}) @@ -973,6 +975,13 @@ function repos_move(varargin) function repos_end(varargin) set(gcbf,'windowbuttonmotionfcn','', 'windowbuttonupfcn',''); +function scroll_wheel(varargin) +ScrollWheelData = varargin{2}; +% detect if mouse is over an axis and change +centre = spm_orthviews('Pos'); +spm_orthviews('setcoords',centre+[0 0 ScrollWheelData.VerticalScrollCount]'); +%spm_orthviews('reposition'); + %========================================================================== % function bbox diff --git a/spm_parrec2nifti.m b/spm_parrec2nifti.m index e8c254ed..dbdf058a 100644 --- a/spm_parrec2nifti.m +++ b/spm_parrec2nifti.m @@ -11,7 +11,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_parrec2nifti.m 6315 2015-01-23 17:09:06Z guillaume $ +% $Id: spm_parrec2nifti.m 6500 2015-07-16 14:26:42Z guillaume $ %-Display warning @@ -50,7 +50,8 @@ %-Write NIfTI header %-------------------------------------------------------------------------- dim = [hdr.ImageInfo(1).recon_resolution hdr.MaxNumberOfSlices hdr.MaxNumberOfDynamics]; -dtype = hdr.ImageInfo(1).image_pixel_size; +dim = double(dim); +dtype = double(hdr.ImageInfo(1).image_pixel_size); if prod(dim)*dtype/8 ~= bytes % can happen for corrupted files or non dynamic data dim(4) = bytes*8/dtype/prod(dim(1:3)); @@ -97,7 +98,8 @@ % should handle interleaved data % data should not be scaled/unscaled for i=1:dim(4) - dato(:,:,:,i) = dati(:,:,:,i); + slice_order = [hdr.ImageInfo([hdr.ImageInfo.dynamic_scan_number]==i).slice_number]; + dato(:,:,:,i) = dati(:,:,slice_order,i); end diff --git a/spm_peb_ppi.m b/spm_peb_ppi.m index 11e9098d..15d2e4f6 100644 --- a/spm_peb_ppi.m +++ b/spm_peb_ppi.m @@ -47,7 +47,7 @@ % Copyright (C) 2002-2014 Wellcome Trust Centre for Neuroimaging % Darren Gitelman -% $Id: spm_peb_ppi.m 5969 2014-05-01 14:37:22Z guillaume $ +% $Id: spm_peb_ppi.m 6556 2015-09-15 15:42:04Z guillaume $ % SETTING UP A PPI THAT ACCOUNTS FOR THE HRF @@ -430,14 +430,13 @@ xn = xb*C{2}.E(1:N); xn = spm_detrend(xn); - % Setup psychological variable from inputs and contast weights + % Setup psychological variable from inputs and contrast weights %---------------------------------------------------------------------- PSY = zeros(N*NT,1); for i = 1:size(U.u,2) PSY = PSY + full(U.u(:,i) * U.w(i)); end - % PSY = spm_detrend(PSY); % removed centering of psych variable prior - % to multiplication with xn in r3271. + PSY = spm_detrend(PSY); % Multiply psychological variable by neural signal %---------------------------------------------------------------------- diff --git a/spm_phase_shuffle.m b/spm_phase_shuffle.m index c4da9ef9..8e3906d5 100644 --- a/spm_phase_shuffle.m +++ b/spm_phase_shuffle.m @@ -4,10 +4,10 @@ % x - data matrix (time-series in columns) % n - optional window length for windowed shuffling %__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2007-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_phase_shuffle.m 3334 2009-08-25 16:13:38Z karl $ +% $Id: spm_phase_shuffle.m 6654 2015-12-22 12:55:36Z spm $ try @@ -17,7 +17,7 @@ k = 1:fix(n/2); for i = 1:size(x,2); C = spm_wft(x(:,i),k,n); - W = abs(C).*exp(j*angle(C(randperm(size(C,1)),:))); + W = abs(C).*exp(1i*angle(C(randperm(size(C,1)),:))); y(:,i) = spm_iwft(W,k,n)'; end @@ -32,7 +32,7 @@ p = zeros(n,size(x,2)); p(i,:) = r; p(n - i + 2,:) = -r; - s = abs(s).*exp(j*p); + s = abs(s).*exp(1i*p); y = real(ifft(s)); end diff --git a/spm_platform.m b/spm_platform.m index 4721a00c..55cf8ce7 100644 --- a/spm_platform.m +++ b/spm_platform.m @@ -53,7 +53,7 @@ % Copyright (C) 1999-2014 Wellcome Trust Centre for Neuroimaging % Matthew Brett -% $Id: spm_platform.m 6245 2014-10-15 11:22:15Z guillaume $ +% $Id: spm_platform.m 6575 2015-10-15 15:22:08Z volkmar $ %-Initialise @@ -259,7 +259,7 @@ %-Desktop %-------------------------------------------------------------------------- try - PLATFORM.desktop = desktop('-inuse'); + PLATFORM.desktop = usejava('desktop'); catch PLATFORM.desktop = false; end diff --git a/spm_plot_ci.m b/spm_plot_ci.m index 48e663ae..22637f74 100644 --- a/spm_plot_ci.m +++ b/spm_plot_ci.m @@ -1,16 +1,34 @@ function spm_plot_ci(E,C,x,j,s) -% plots mean and conditional confidence intervals +% Plot mean and conditional confidence intervals % FORMAT spm_plot_ci(E,C,x,j,s) % E - expectation (structure or array) % C - variance or covariance (structure or array) % x - domain % j - rows of E to plot % s - string to specify plot type:e.g. '--r' or 'exp' +% +% If E is a row vector with two elements, confidence regions will be +% plotted; otherwise, bar charts with confidence intervals are provided %__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_plot_ci.m 6341 2015-02-18 14:46:43Z karl $ +% $Id: spm_plot_ci.m 6587 2015-11-02 10:29:49Z karl $ + + +% get axis +%-------------------------------------------------------------------------- +ax = gca; + +% confidence region plotting +%-------------------------------------------------------------------------- +if size(E,1) == 1 && size(E,2) == 2 + E = E'; + CR = true; +else + CR = false; +end + % unpack expectations into a matrix %-------------------------------------------------------------------------- @@ -63,6 +81,8 @@ function spm_plot_ci(E,C,x,j,s) %---------------------------------------------------------------------- if all(size(C) == size(E)) c = ci*sqrt(C(j,:)); + elseif all(size(C') == size(E)) + c = ci*sqrt(C(:,j)); else % try covariance matrix @@ -75,8 +95,8 @@ function spm_plot_ci(E,C,x,j,s) % set plot parameters %-------------------------------------------------------------------------- -switch get(gca,'NextPlot') - case{lower('add')} +switch lower(get(ax,'NextPlot')) + case 'add' col = [1 1/4 1/4]; width = .9; otherwise @@ -84,7 +104,19 @@ function spm_plot_ci(E,C,x,j,s) width = .8; end -% conditional covariances +% plot elliptical confidence region +%-------------------------------------------------------------------------- +if CR + [x,y] = ellipsoid(E(1),E(2),1,c(1),c(2),0,32); + fill(x(16,:)',y(16,:)',[1 1 1]*gr,'EdgeColor',[1 1 1]*.5,'Parent',ax); + hold(ax,'on'); + plot(ax,E(1),E(2),'.','MarkerSize',16); + hold(ax,'off'); drawnow + return +end + + +% plot bar chart %-------------------------------------------------------------------------- if N >= 8 @@ -92,25 +124,16 @@ function spm_plot_ci(E,C,x,j,s) %====================================================================== if strcmpi(s,'exp') fill([x fliplr(x)],exp([full(E + c) fliplr(full(E - c))]),... - [.95 .95 1],'EdgeColor',[.8 .8 1]),hold on - plot(x,exp(E)) + [.95 .95 1],'EdgeColor',[.8 .8 1],'Parent',ax); + hold(ax,'on'); + plot(x,exp(E)); else fill([x fliplr(x)],[full(E + c) fliplr(full(E - c))],... - [.95 .95 1],'EdgeColor',[.8 .8 1]),hold on - plot(x,E,s) - end - - -elseif n == 2 - - % plot in state-space - %====================================================================== - try, C = C{1}; end - [x,y] = ellipsoid(E(1),E(2),1,c(1),c(2),0,32); - fill(x(16,:)',y(16,:)',[1 1 1]*gr,'EdgeColor',[1 1 1]*.5),hold on - plot(E(1,1),E(2,1),'.','MarkerSize',16) - + [.95 .95 1],'EdgeColor',[.8 .8 1],'Parent',ax); + hold(ax,'on'); + plot(ax,x,E,s); + end else @@ -123,13 +146,13 @@ function spm_plot_ci(E,C,x,j,s) % conditional means %-------------------------------------------------------------- - bar(exp(E),width,'Edgecolor',[1 1 1]/2,'Facecolor',[1 1 1]*.8) - hold on + bar(ax,exp(E),width,'Edgecolor',[1 1 1]/2,'Facecolor',[1 1 1]*.8); + hold(ax,'on'); % conditional variances %-------------------------------------------------------------- for k = 1:n - line([k k],exp([-1 1]*c(k) + E(k)),'LineWidth',4,'Color',col); + line([k k],exp([-1 1]*c(k) + E(k)),'LineWidth',4,'Color',col,'Parent',ax); end else @@ -137,45 +160,45 @@ function spm_plot_ci(E,C,x,j,s) if n > 1 % conditional means - %-------------------------------------------------------------- - bar(E,width,'Edgecolor',[1 1 1]/2,'Facecolor',[1 1 1]*.8) - hold on + %---------------------------------------------------------- + bar(ax,E,width,'Edgecolor',[1 1 1]/2,'Facecolor',[1 1 1]*.8); + hold(ax,'on'); else % conditional means - %-------------------------------------------------------------- - bar(E,'Edgecolor',[1 1 1]/2,'Facecolor',[1 1 1]*.8) - hold on + %---------------------------------------------------------- + bar(ax,E,'Edgecolor',[1 1 1]/2,'Facecolor',[1 1 1]*.8); + hold(ax,'on'); end % conditional variances %-------------------------------------------------------------- for k = 1:n - line([k k],[-1 1]*c(k) + E(k),'LineWidth',4,'Color',col); + line([k k],[-1 1]*c(k) + E(k),'LineWidth',4,'Color',col,'Parent',ax); end end - box off - set(gca,'XLim',[0 n + 1]) + box(ax,'off'); + set(ax,'XLim',[0 n + 1]); else % conditional means %------------------------------------------------------------------ - h = bar(E); hold on + h = bar(ax,E); hold(ax,'on'); % conditional variances %------------------------------------------------------------------ for m = 1:N x = mean(get(get(h(m),'Children'),'Xdata')); for k = 1:n - line([x(k) x(k)],[-1 1]*c(k,m) + E(k,m),'LineWidth',4,'Color',col); + line([x(k) x(k)],[-1 1]*c(k,m) + E(k,m),'LineWidth',4,'Color',col,'Parent',ax); end end end end -hold off -drawnow \ No newline at end of file +hold(ax,'off'); +drawnow diff --git a/spm_project.mexw32 b/spm_project.mexw32 index 66c8f7de..5131a785 100755 Binary files a/spm_project.mexw32 and b/spm_project.mexw32 differ diff --git a/spm_project.mexw64 b/spm_project.mexw64 index 62535144..3f976a35 100755 Binary files a/spm_project.mexw64 and b/spm_project.mexw64 differ diff --git a/spm_provenance.m b/spm_provenance.m index e33baacc..8b07d0e6 100644 --- a/spm_provenance.m +++ b/spm_provenance.m @@ -6,7 +6,8 @@ % p.get_default_namespace % p.set_default_namespace(uri) % p.add_namespace(prefix,uri) -% p.get_namespace +% p.get_namespace(prefix) +% p.remove_namespace(prefix) % p.entity(id,attributes) % p.activity(id,startTime,endTime,attributes) % p.agent(id,attributes) @@ -31,10 +32,10 @@ % p.hadMember(collection,entity) % p.bundle(id,b) %__________________________________________________________________________ -% Copyright (C) 2013-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2013-2015 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_provenance.m 6425 2015-04-29 18:24:51Z guillaume $ +% $Id: spm_provenance.m 6646 2015-12-14 19:00:26Z guillaume $ %-Properties @@ -83,11 +84,21 @@ function set_default_namespace(obj,uri) end function uri = get_namespace(obj,prefix) + if nargin == 1, uri = obj.namespace; return; end n = ismember({obj.namespace.prefix},prefix); if ~any(n), uri = ''; else uri = obj.namespace(n).uri; end end + function remove_namespace(obj,prefix) + n = ismember({obj.namespace.prefix},prefix); + if any(n) + obj.namespace(n) = []; + else + warning('Prefix ''%s'' not found.',prefix); + end + end + %-Components %---------------------------------------------------------------------- %function entity(obj,id,attributes) @@ -479,11 +490,13 @@ function addItem(obj,varargin) o = blanks(2*step); str = ''; %-Namespace - if step ==1 + %if step == 1 % if step > 1, only write user defined namespaces ns = obj.namespace; - ns(end+1) = struct('prefix','rdfs','uri','http://www.w3.org/2000/01/rdf-schema#'); - if ~isempty(ns(1).uri) - str = [str sprintf('@prefix : <%s> .\n',ns(1).uri)]; + if step == 1 + ns(end+1) = struct('prefix','rdfs','uri','http://www.w3.org/2000/01/rdf-schema#'); + if ~isempty(ns(1).uri) + str = [str sprintf('@prefix : <%s> .\n',ns(1).uri)]; + end end for i=2:numel(ns) str = [str sprintf('@prefix %s: <%s> .\n',ns(i).prefix,ns(i).uri)]; @@ -491,7 +504,7 @@ function addItem(obj,varargin) if ~isempty(ns(1).uri) || numel(ns) > 3 str = [str sprintf('\n')]; end - end + %end %-Expressions % optional entries for activity and relations are not saved for i=1:numel(obj.stack) diff --git a/spm_rand_power_law.m b/spm_rand_power_law.m index cc0ca090..a787abf1 100644 --- a/spm_rand_power_law.m +++ b/spm_rand_power_law.m @@ -1,17 +1,20 @@ -function [y] = spm_rand_power_law(csd,Hz,dt,N) +function [y,K] = spm_rand_power_law(csd,Hz,dt,N) % generates random variates with a power law spectral density -% FORMAT [y] = spm_rand_power_law(csd,Hz,dt,N) -% G - spectral densities (one per row) +% FORMAT [y,K] = spm_rand_power_law(csd,Hz,dt,N) +% csd - spectral densities (one per row) % Hz - frequencies % dt - sampling interval % N - number of time bins % +% y - random variate +% K - convolution (kernel) operator: y(:,i) = K*randn(N,1) +% % see also: spm_rand_mar; spm_Q %__________________________________________________________________________ % Copyright (C) 2012 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_rand_power_law.m 6254 2014-11-04 18:24:21Z karl $ +% $Id: spm_rand_power_law.m 6481 2015-06-16 17:01:47Z karl $ % create random process @@ -28,17 +31,23 @@ y(:,i) = P\randn(N,1)*sqrt(max(ccf)); end +% convolution kernel +%-------------------------------------------------------------------------- +if nargout > 1 + K = inv(P)*sqrt(max(ccf)); +end + return -% cNB: alternative scheme - create random process +% NB: alternative scheme - create random process %========================================================================== -[m n] = size(G); +[m n] = size(csd); w = (0:(N - 1))/dt/N; dHz = Hz(2) - Hz(1); g = zeros(N,n); for i = 1:m j = find(w > Hz(i),1); - s = sqrt(G(i,:)).*(randn(1,n) + 1j*randn(1,n)); + s = sqrt(csd(i,:)).*(randn(1,n) + 1j*randn(1,n)); g(j,:) = s; j = N - j + 2; g(j,:) = conj(s); diff --git a/spm_render.m b/spm_render.m index b4b29664..c2e60d36 100644 --- a/spm_render.m +++ b/spm_render.m @@ -30,12 +30,12 @@ % are 10mm behind the surface have half the intensity of ones at the % surface. %__________________________________________________________________________ -% Copyright (C) 1996-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1996-2015 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_render.m 6230 2014-10-06 16:22:38Z guillaume $ +% $Id: spm_render.m 6510 2015-07-31 14:49:33Z guillaume $ -SVNrev = '$Rev: 6230 $'; +SVNrev = '$Rev: 6510 $'; global prevrend if ~isstruct(prevrend) @@ -130,7 +130,7 @@ col = hot(256); else col = eye(3); - if spm_input('Which colours?','!+1','b',{'RGB','Custom'},[0 1],1) + if spm_input('Which colours?','+1','b',{'RGB','Custom'},[0 1],1) for k = 1:num col(k,:) = uisetcolor(col(k,:),sprintf('Colour of blob set %d',k)); end @@ -149,11 +149,11 @@ end if isfinite(brt) - brt = spm_input('Brighten blobs',1,'none|slightly|more|lots',[1 0.75 0.5 0.25], 1); + brt = spm_input('Brighten blobs','+1','none|slightly|more|lots',[1 0.75 0.5 0.25], 1); col = eye(3); % ask for custom colours & get rgb values %------------------------------------------------------------------ - if spm_input('Which colours?','!+1','b',{'RGB','Custom'},[0 1],1) + if spm_input('Which colours?','+1','b',{'RGB','Custom'},[0 1],1) for k = 1:num col(k,:) = uisetcolor(col(k,:),sprintf('Colour of blob set %d',k)); end @@ -357,7 +357,9 @@ if nargout for i=1:numel(rgb) - rgb{i} = flipud(rgb{i}); + for ch=1:size(rgb{i},3) + rgb{i}(:,:,ch) = flipud(rgb{i}(:,:,ch)); + end end varargout = { rgb }; end diff --git a/spm_render_vol.mexa64 b/spm_render_vol.mexa64 index 8dab99b8..3cc6e11f 100755 Binary files a/spm_render_vol.mexa64 and b/spm_render_vol.mexa64 differ diff --git a/spm_render_vol.mexmaci64 b/spm_render_vol.mexmaci64 index e6aebd2c..47cce538 100755 Binary files a/spm_render_vol.mexmaci64 and b/spm_render_vol.mexmaci64 differ diff --git a/spm_render_vol.mexw32 b/spm_render_vol.mexw32 index 40c50d29..3306c5b3 100755 Binary files a/spm_render_vol.mexw32 and b/spm_render_vol.mexw32 differ diff --git a/spm_render_vol.mexw64 b/spm_render_vol.mexw64 index 03e767a0..b2a6c8c9 100755 Binary files a/spm_render_vol.mexw64 and b/spm_render_vol.mexw64 differ diff --git a/spm_resels_vol.mexa64 b/spm_resels_vol.mexa64 index 5d7716c0..94597860 100755 Binary files a/spm_resels_vol.mexa64 and b/spm_resels_vol.mexa64 differ diff --git a/spm_resels_vol.mexmaci64 b/spm_resels_vol.mexmaci64 index 30c12a04..bd5152cd 100755 Binary files a/spm_resels_vol.mexmaci64 and b/spm_resels_vol.mexmaci64 differ diff --git a/spm_resels_vol.mexw32 b/spm_resels_vol.mexw32 index 933072f4..8093e6f3 100755 Binary files a/spm_resels_vol.mexw32 and b/spm_resels_vol.mexw32 differ diff --git a/spm_resels_vol.mexw64 b/spm_resels_vol.mexw64 index b0839120..b3f3da4a 100755 Binary files a/spm_resels_vol.mexw64 and b/spm_resels_vol.mexw64 differ diff --git a/spm_results_nidm.m b/spm_results_nidm.m index 6bea2199..f0129160 100644 --- a/spm_results_nidm.m +++ b/spm_results_nidm.m @@ -1,12 +1,12 @@ -function [outdir, prov] = spm_results_nidm(SPM,xSPM,TabDat) +function [nidmfile, prov] = spm_results_nidm(SPM,xSPM,TabDat) % Export SPM stats results using the Neuroimaging Data Model (NIDM) -% FORMAT [outdir, prov] = spm_results_nidm(SPM,xSPM,TabDat) -% SPM - structure containing analysis details (see spm_spm.m) -% xSPM - structure containing inference details (see spm_getSPM.m) -% TabDat - structure containing results details (see spm_list.m) +% FORMAT [nidmfile, prov] = spm_results_nidm(SPM,xSPM,TabDat) +% SPM - structure containing analysis details (see spm_spm.m) +% xSPM - structure containing inference details (see spm_getSPM.m) +% TabDat - structure containing results details (see spm_list.m) % -% outdir - output directory -% prov - provenance object (see spm_provenance.m) +% nidmfile - output NIDM zip archive filename +% prov - provenance object (see spm_provenance.m) %__________________________________________________________________________ % References: % @@ -16,10 +16,10 @@ % PROV-DM: The PROV Data Model: % http://www.w3.org/TR/prov-dm/ %__________________________________________________________________________ -% Copyright (C) 2013-2015 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2013-2016 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_results_nidm.m 6468 2015-06-03 16:22:52Z guillaume $ +% $Id: spm_results_nidm.m 6681 2016-01-15 13:21:00Z guillaume $ %-Get input parameters, interactively if needed @@ -42,17 +42,26 @@ %-Options %-------------------------------------------------------------------------- gz = '.gz'; %-Compressed NIfTI {'.gz', ''} -coordsys = nidm_conv('nidm:MNICoordinateSystem'); %-Assuming MNI space -NIDMversion = '1.1.0'; - +coordsys = 'nidm_MNICoordinateSystem'; %-Assuming MNI space +NIDMversion = '1.2.0'; +SVNrev = '$Rev: 6681 $'; + +try + units = xSPM.units; +catch + try + units = SPM.xVol.units; + catch + units = {'mm' 'mm' 'mm'}; + end +end %========================================================================== %-Populate output directory %========================================================================== if ~exist(SPM.swd,'dir'), SPM.swd = pwd; end -outdir = fullfile(SPM.swd,'nidm'); -outdir = spm_file(outdir,'uniquedir'); -sts = mkdir(outdir); +outdir = tempname(SPM.swd); +sts = mkdir(outdir); if ~sts, error('Cannot create directory "%s".',outdir); end %-Design Matrix image (as png and csv) @@ -68,7 +77,7 @@ %-Maximum Intensity Projection image (as png) %-------------------------------------------------------------------------- files.mip = fullfile(outdir,'MaximumIntensityProjection.png'); -MIP = spm_mip(xSPM.Z,xSPM.XYZmm,xSPM.M,xSPM.units); +MIP = spm_mip(xSPM.Z,xSPM.XYZmm,xSPM.M,units); imwrite(MIP,gray(64),files.mip,'png'); %-Beta images (as NIfTI) @@ -79,7 +88,7 @@ % end files.beta = {}; -%-SPM{.}, contrast, standard error and ESS images (as NIfTI) +%-SPM{.}, contrast, contrast standard error, and contrast explained mean square images (as NIfTI) %-------------------------------------------------------------------------- for i=1:numel(xSPM.Ic) if numel(xSPM.Ic) == 1, postfix = ''; @@ -92,10 +101,11 @@ struct('STAT','con')); files.conse{i} = fullfile(outdir,['ContrastStandardError' postfix '.nii' gz]); Vc = SPM.xCon(xSPM.Ic(i)).c' * SPM.xX.Bcov * SPM.xCon(xSPM.Ic(i)).c; - img2nii(fullfile(xSPM.swd,SPM.VResMS.fname), files.conse{i}, struct('fcn',@(x) sqrt(x*Vc))); + img2nii(fullfile(xSPM.swd,SPM.VResMS.fname), files.conse{i}, struct('fcn',@(x) sqrt(x*Vc))); elseif xSPM.STAT == 'F' - files.ess{i} = fullfile(outdir,['ExtraSumOfSquares' postfix '.nii' gz]); - img2nii(fullfile(xSPM.swd,SPM.xCon(xSPM.Ic(i)).Vcon.fname), files.ess{i}); + files.effms{i} = fullfile(outdir,['ContrastExplainedMeanSquare' postfix '.nii' gz]); + eidf = SPM.xCon(xSPM.Ic(i)).eidf; + img2nii(fullfile(xSPM.swd,SPM.xCon(xSPM.Ic(i)).Vcon.fname), files.effms{i}, struct('fcn',@(x) x/eidf)); end end @@ -137,14 +147,19 @@ Vgm = spm_create_vol(Vgm); Vgm.pinfo(1,1) = spm_add(Vb,Vgm); Vgm = spm_create_vol(Vgm); -grandMeanMedian = spm_summarise(files.grandmean,SPM.VM.fname,@median); +grandMeanMedian = spm_summarise(files.grandmean,fullfile(xSPM.swd,SPM.VM.fname),@median); if ~isempty(gz), gzip(files.grandmean); spm_unlink(files.grandmean); files.grandmean = [files.grandmean gz]; end %-Explicit mask image (as NIfTI) %-------------------------------------------------------------------------- if ~isempty(SPM.xM.VM) files.emask = fullfile(outdir,['CustomMask.nii' gz]); - img2nii(fullfile(xSPM.swd,SPM.xM.VM.fname), files.emask); + if isempty(spm_file(SPM.xM.VM.fname,'path')) + Vem = fullfile(xSPM.swd,SPM.xM.VM.fname); + else + Vem = SPM.xM.VM.fname; + end + img2nii(Vem, files.emask); else files.emask = ''; end @@ -215,7 +230,7 @@ clear coordspace originalfile isHumanReadable niifmt = {'image/nifti','xsd:string'}; -isHumanReadable(true); +isHumanReadable(false); pp = spm_provenance; @@ -234,48 +249,67 @@ %-Provenance %-------------------------------------------------------------------------- +[V,R] = spm('Ver'); + idResults = getid('niiri:spm_results_id',isHumanReadable); pp.entity(idResults,{... 'prov:type','prov:Bundle',... - 'prov:type',nidm_conv('nidm:Results'),... + 'prov:type',nidm_conv('nidm_NIDMResults',pp),... 'prov:label','NIDM-Results',... - nidm_conv('nidm:version'),{NIDMversion,'xsd:string'},... + nidm_conv('nidm_version',pp),{NIDMversion,'xsd:string'},... }); -pp.wasGeneratedBy(idResults,'-',now); + +idExporter = getid('niiri:exporter_id',isHumanReadable); +pp.agent(idExporter,{... + 'prov:type',nidm_conv('nidm_spm_results_nidm',pp),... + 'prov:type','prov:SoftwareAgent',... + 'prov:label',{'spm_results_nidm','xsd:string'},... + nidm_conv('nidm_softwareVersion',pp),{[V(4:end) '.' char(regexp(SVNrev,'\$Rev: (\d.*?) \$','tokens','once'))],'xsd:string'},... + }); + +idExport = getid('niiri:export_id',isHumanReadable); +pp.activity(idExport,{... + 'prov:type',nidm_conv('nidm_NIDMResultsExport',pp),... + 'prov:label','NIDM-Results export',... + }); +pp.wasAssociatedWith(idExport, idExporter); +pp.wasGeneratedBy(idResults, idExport, now); p = spm_provenance; +p.remove_namespace('prov'); +p.remove_namespace('xsd'); +coordsys = nidm_conv(coordsys,p); %-Agent: SPM %-------------------------------------------------------------------------- -[V,R] = spm('Ver'); idSoftware = getid('niiri:software_id',isHumanReadable); p.agent(idSoftware,{... - 'prov:type',nidm_conv('neurolex:SPM'),... + 'prov:type',nidm_conv('neurolex_SPM',p),... 'prov:type','prov:SoftwareAgent',... 'prov:label',{'SPM','xsd:string'},... - nidm_conv('nidm:softwareVersion'),{[V(4:end) '.' R],'xsd:string'},... + nidm_conv('nidm_softwareVersion',p),{[V(4:end) '.' R],'xsd:string'},... }); %-Entity: Coordinate Space %-------------------------------------------------------------------------- -id_data_coordspace = coordspace(p,xSPM.M,xSPM.DIM,xSPM.units,coordsys,1); +id_data_coordspace = coordspace(p,xSPM.M,xSPM.DIM,units,coordsys,1); %-Entity: Image Data %-------------------------------------------------------------------------- if isfield(SPM,'Sess') extra_fields = {... - nidm_conv('nidm:grandMeanScaling'),{'true','xsd:boolean'},... - nidm_conv('nidm:targetIntensity'),{SPM.xGX.GM,'xsd:float'},... + nidm_conv('nidm_grandMeanScaling',p),{'true','xsd:boolean'},... + nidm_conv('nidm_targetIntensity',p),{SPM.xGX.GM,'xsd:float'},... }; else extra_fields = {... - nidm_conv('nidm:grandMeanScaling'),{'false','xsd:boolean'},... + nidm_conv('nidm_grandMeanScaling',p),{'false','xsd:boolean'},... }; end idData = getid('niiri:data_id',isHumanReadable); p.entity(idData,{... 'prov:type','prov:Collection',... - 'prov:type',nidm_conv('nidm:Data'),... + 'prov:type',nidm_conv('nidm_DataScaling',p),... 'prov:label',{'Data','xsd:string'},... extra_fields{:}}); @@ -285,11 +319,11 @@ idDriftModel = getid('niiri:drift_model_id',isHumanReadable); p.entity(idDriftModel,{... - 'prov:type',nidm_conv('spm:DCTDriftModel'),... + 'prov:type',nidm_conv('spm_DCTDriftModel',p),... 'prov:label','SPM''s DCT Drift Model',... - nidm_conv('spm:driftCutoffPeriod'),{SPM.xX.K(1).HParam,'xsd:float'},... + nidm_conv('spm_SPMsDriftCutoffPeriod',p),{SPM.xX.K(1).HParam,'xsd:float'},... }); - extra_fields_drift = {nidm_conv('nidm:hasDriftModel'),idDriftModel}; + extra_fields_drift = {nidm_conv('nidm_hasDriftModel',p),idDriftModel}; else extra_fields_drift = {}; end @@ -304,16 +338,16 @@ switch SPM.xBF.name case 'hrf' extra_fields_basis_set = ... - {nidm_conv('nidm:hasHRFBasis'),nidm_conv('spm:GammaDifferenceHRF')}; + {nidm_conv('nidm_hasHRFBasis',p),nidm_conv('spm_SPMsCanonicalHRF',p)}; case 'hrf (with time derivative)' extra_fields_basis_set = {... - nidm_conv('nidm:hasHRFBasis'),nidm_conv('spm:GammaDifferenceHRF'),... - nidm_conv('nidm:hasHRFBasis'),nidm_conv('spm:TemporalDerivative')}; + nidm_conv('nidm_hasHRFBasis',p),nidm_conv('spm_SPMsCanonicalHRF',p),... + nidm_conv('nidm_hasHRFBasis',p),nidm_conv('spm_SPMsTemporalDerivative',p)}; case 'hrf (with time and dispersion derivatives)' extra_fields_basis_set = {... - nidm_conv('nidm:hasHRFBasis'),nidm_conv('spm:GammaDifferenceHRF'),... - nidm_conv('nidm:hasHRFBasis'),nidm_conv('spm:TemporalDerivative'),... - nidm_conv('nidm:hasHRFBasis'),nidm_conv('spm:DispersionDerivative')}; + nidm_conv('nidm_hasHRFBasis',p),nidm_conv('spm_SPMsCanonicalHRF',p),... + nidm_conv('nidm_hasHRFBasis',p),nidm_conv('spm_SPMsTemporalDerivative',p),... + nidm_conv('nidm_hasHRFBasis',p),nidm_conv('spm_SPMsDispersionDerivative',p)}; case {'Fourier set','Fourier set (Hanning)','Gamma functions','Finite Impulse Response'} warning('Not implemented yet.'); otherwise @@ -322,17 +356,16 @@ end p.entity(idDesignMatrix,{... - 'prov:type',nidm_conv('nidm:DesignMatrix'),... + 'prov:type',nidm_conv('nidm_DesignMatrix',p),... 'prov:location',{uri(spm_file(files.descsv,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.descsv,'filename'),'xsd:string'},... 'dct:format',{'text/csv','xsd:string'},... 'dc:description',idDesignMatrixImage,... 'prov:label',{'Design Matrix','xsd:string'},... - nidm_conv('nidm:regressorNames'),{SPM.xX.name,'xsd:string'},... + nidm_conv('nidm_regressorNames',p),{nidm_esc(SPM.xX.name),'xsd:string'},... extra_fields_drift{:},... extra_fields_basis_set{:} }); - %nidm_conv('nidm:hasfMRIDesign'),nidm_conv('nidm:BlockDesign'),... p.entity(idDesignMatrixImage,{... 'prov:type','dctype:Image',... @@ -347,19 +380,21 @@ if ~spm_check_orientations(struct('dim',{xSPM.DIM',SPM.xM.VM.dim},... 'mat',{xSPM.M,SPM.xM.VM.mat}),false) id_emask_coordspace = coordspace(p,SPM.xM.VM.mat,SPM.xM.VM.dim',... - xSPM.units,coordsys); + units,coordsys); + else + id_emask_coordspace = id_data_coordspace; end idMask2 = getid('niiri:mask_id_2',isHumanReadable); p.entity(idMask2,{... - 'prov:type','nidm:CustomMaskMap',... + 'prov:type',nidm_conv('nidm_MaskMap',p),... % CustomMaskMap 'prov:location',{uri(spm_file(files.emask,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.emask,'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Custom Mask','xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),id_emask_coordspace,... + nidm_conv('nidm_inCoordinateSpace',p),id_emask_coordspace,... 'crypto:sha512',{sha512sum(spm_file(files.emask,'cpath')),'xsd:string'},... }); - id = originalfile(p,files.emask,idMask2,'nidm:CustomMaskMap'); + id = originalfile(p,files.emask,idMask2,nidm_conv('nidm_MaskMap',p)); % CustomMaskMap p.wasDerivedFrom(idMask2,id); end @@ -368,55 +403,55 @@ if isfield(SPM.xVi,'form') if strcmp(SPM.xVi.form,'i.i.d') extra_fields_NM = {... - nidm_conv('nidm:hasErrorDependence'),nidm_conv('nidm:IndependentError'),... - nidm_conv('nidm:errorVarianceHomogeneous'),{'true','xsd:boolean'},... + nidm_conv('nidm_hasErrorDependence',p),nidm_conv('nidm_IndependentError',p),... + nidm_conv('nidm_errorVarianceHomogeneous',p),{'true','xsd:boolean'},... }; extra_fields_PE = { - nidm_conv('nidm:withEstimationMethod'),nidm_conv('stato:OLS'),... + nidm_conv('nidm_withEstimationMethod',p),nidm_conv('stato_OLS',p),... }; else extra_fields_NM = {... - nidm_conv('nidm:hasErrorDependence'),nidm_conv('stato:ToeplitzCovarianceStructure'),... - nidm_conv('nidm:dependenceSpatialModel'),nidm_conv('nidm:SpatiallyGlobalModel'),... - nidm_conv('nidm:errorVarianceHomogeneous'),{'true','xsd:boolean'},... - nidm_conv('nidm:varianceSpatialModel'),nidm_conv('nidm:SpatiallyLocalModel'),... + nidm_conv('nidm_hasErrorDependence',p),nidm_conv('stato_ToeplitzCovarianceStructure',p),... + nidm_conv('nidm_dependenceMapWiseDependence',p),nidm_conv('nidm_ConstantParameter',p),... + nidm_conv('nidm_errorVarianceHomogeneous',p),{'true','xsd:boolean'},... + nidm_conv('nidm_varianceMapWiseDependence',p),nidm_conv('nidm_IndependentParameter',p),... }; extra_fields_PE = { - nidm_conv('nidm:withEstimationMethod'),nidm_conv('stato:GLS'),... + nidm_conv('nidm_withEstimationMethod',p),nidm_conv('stato_GLS',p),... }; end else if ~isfield(SPM.xVi,'Vi') || numel(SPM.xVi.Vi) == 1 % assume it's identity extra_fields_NM = {... - nidm_conv('nidm:hasErrorDependence'),nidm_conv('nidm:IndependentError'),... - nidm_conv('nidm:errorVarianceHomogeneous'),{'true','xsd:boolean'},... + nidm_conv('nidm_hasErrorDependence',p),nidm_conv('nidm_IndependentError',p),... + nidm_conv('nidm_errorVarianceHomogeneous',p),{'true','xsd:boolean'},... }; extra_fields_PE = { - nidm_conv('nidm:withEstimationMethod'),nidm_conv('stato:OLS'),... + nidm_conv('nidm_withEstimationMethod',p),nidm_conv('stato_OLS',p),... }; else extra_fields_NM = {... - nidm_conv('nidm:hasErrorDependence'),'nidm:ArbitrarilyCorrelatedError',... - 'nidm:dependenceSpatialModel','nidm:SpatiallyGlobalModel',... - 'nidm:errorVarianceHomogeneous',{'false','xsd:boolean'},... - 'nidm:varianceSpatialModel','nidm:SpatiallyLocalModel',... + nidm_conv('nidm_hasErrorDependence',p),nidm_conv('stato_UnstructuredCorrelationStructure',p),... + nidm_conv('nidm_dependenceMapWiseDependence',p),nidm_conv('nidm_ConstantParameter',p),... + nidm_conv('nidm_errorVarianceHomogeneous',p),{'false','xsd:boolean'},... + nidm_conv('nidm_varianceMapWiseDependence',p),nidm_conv('nidm_IndependentParameter',p),... }; extra_fields_PE = { - nidm_conv('nidm:withEstimationMethod'),nidm_conv('stato:GLS'),... + nidm_conv('nidm_withEstimationMethod',p),nidm_conv('stato_GLS',p),... }; end end idErrorModel = getid('niiri:error_model_id',isHumanReadable); p.entity(idErrorModel,{... - 'prov:type',nidm_conv('nidm:ErrorModel'),... - nidm_conv('nidm:hasErrorDistribution'),nidm_conv('nidm:GaussianDistribution'),... + 'prov:type',nidm_conv('nidm_ErrorModel',p),... + nidm_conv('nidm_hasErrorDistribution',p),nidm_conv('stato_GaussianDistribution',p),... extra_fields_NM{:}}); %-Activity: Model Parameters Estimation %========================================================================== idModelPE = getid('niiri:model_pe_id',isHumanReadable); p.activity(idModelPE,{... - 'prov:type',nidm_conv('nidm:ModelParametersEstimation'),... + 'prov:type',nidm_conv('nidm_ModelParametersEstimation',p),... 'prov:label','Model parameters estimation',... extra_fields_PE{:}}); p.wasAssociatedWith(idModelPE, idSoftware); @@ -431,16 +466,16 @@ %-------------------------------------------------------------------------- idMask1 = getid('niiri:mask_id_1',isHumanReadable); p.entity(idMask1,{... - 'prov:type',nidm_conv('nidm:MaskMap'),... + 'prov:type',nidm_conv('nidm_MaskMap',p),... 'prov:location',{uri(spm_file(files.mask,'cpath')),'xsd:anyURI'},... - nidm_conv('nidm:isUserDefined'),{'false','xsd:boolean'},... + nidm_conv('nidm_isUserDefined',p),{'false','xsd:boolean'},... 'nfo:fileName',{spm_file(files.mask,'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Mask','xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... 'crypto:sha512',{sha512sum(spm_file(files.mask,'cpath')),'xsd:string'},... }); -id = originalfile(p,SPM.VM.fname,idMask1,nidm_conv('nidm:MaskMap')); +id = originalfile(p,fullfile(SPM.swd,SPM.VM.fname),idMask1,nidm_conv('nidm_MaskMap',p)); p.wasDerivedFrom(idMask1,id); p.wasGeneratedBy(idMask1, idModelPE); @@ -448,13 +483,13 @@ %-------------------------------------------------------------------------- idGrandMean = getid('niiri:grand_mean_map_id',isHumanReadable); p.entity(idGrandMean,{... - 'prov:type',nidm_conv('nidm:GrandMeanMap'),... + 'prov:type',nidm_conv('nidm_GrandMeanMap',p),... 'prov:location',{uri(spm_file(files.grandmean,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.grandmean,'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Grand Mean Map','xsd:string'},... - nidm_conv('nidm:maskedMedian'),{grandMeanMedian,'xsd:float'},... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + nidm_conv('nidm_maskedMedian',p),{grandMeanMedian,'xsd:float'},... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... 'crypto:sha512',{sha512sum(spm_file(files.grandmean,'cpath')),'xsd:string'},... }); p.wasGeneratedBy(idGrandMean, idModelPE); @@ -475,12 +510,12 @@ end idBeta{i} = getid(sprintf('niiri:beta_map_id_%d',i),isHumanReadable); p.entity(idBeta{i},{... - 'prov:type',nidm_conv('nidm:ParameterEstimateMap'),... + 'prov:type',nidm_conv('nidm_ParameterEstimateMap',p),... 'prov:label',{sprintf('Beta Map %d',i),'xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... extra_fields{:},... }); - id = originalfile(p,SPM.Vbeta(i).fname,idBeta{i},nidm_conv('nidm:ParameterEstimateMap')); + id = originalfile(p,SPM.Vbeta(i).fname,idBeta{i},nidm_conv('nidm_ParameterEstimateMap',p)); p.wasDerivedFrom(idBeta{i},id); p.wasGeneratedBy(idBeta{i}, idModelPE); end @@ -489,15 +524,15 @@ %-------------------------------------------------------------------------- idResMS = getid('niiri:residual_mean_squares_map_id',isHumanReadable); p.entity(idResMS,{... - 'prov:type',nidm_conv('nidm:ResidualMeanSquaresMap'),... + 'prov:type',nidm_conv('nidm_ResidualMeanSquaresMap',p),... 'prov:location',{uri(spm_file(files.resms,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.resms,'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Residual Mean Squares Map','xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... 'crypto:sha512',{sha512sum(spm_file(files.resms,'cpath')),'xsd:string'},... }); -id = originalfile(p,SPM.VResMS.fname,idResMS,nidm_conv('nidm:ResidualMeanSquaresMap')); +id = originalfile(p,fullfile(SPM.swd,SPM.VResMS.fname),idResMS,nidm_conv('nidm_ResidualMeanSquaresMap',p)); p.wasDerivedFrom(idResMS,id); p.wasGeneratedBy(idResMS, idModelPE); @@ -505,15 +540,15 @@ %-------------------------------------------------------------------------- idRPV = getid('niiri:resels_per_voxel_map_id',isHumanReadable); p.entity(idRPV,{... - 'prov:type',nidm_conv('nidm:ReselsPerVoxelMap'),... + 'prov:type',nidm_conv('nidm_ReselsPerVoxelMap',p),... 'prov:location',{uri(spm_file(files.rpv,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.rpv,'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Resels per Voxel Map','xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... 'crypto:sha512',{sha512sum(spm_file(files.rpv,'cpath')),'xsd:string'},... }); -id = originalfile(p,SPM.xVol.VRpv.fname,idRPV,nidm_conv('nidm:ReselsPerVoxelMap')); +id = originalfile(p,fullfile(SPM.swd,SPM.xVol.VRpv.fname),idRPV,nidm_conv('nidm_ReselsPerVoxelMap',p)); p.wasDerivedFrom(idRPV,id); p.wasGeneratedBy(idRPV, idModelPE); @@ -525,16 +560,16 @@ idConVec = getid(['niiri:contrast_id' postfix],isHumanReadable); p.entity(idConVec,{... - 'prov:type',nidm_conv('stato:ContrastWeightMatrix'),... - nidm_conv('nidm:statisticType'),nidm_conv(['stato:' xSPM.STAT 'Statistic']),... - nidm_conv('nidm:contrastName'),{SPM.xCon(xSPM.Ic(c)).name,'xsd:string'},... %esc - 'prov:label',{['Contrast: ' SPM.xCon(xSPM.Ic(c)).name],'xsd:string'},... %esc + 'prov:type',nidm_conv('stato_ContrastWeightMatrix',p),... + nidm_conv('nidm_statisticType',p),nidm_conv(['stato_' xSPM.STAT 'Statistic'],p),... + nidm_conv('nidm_contrastName',p),{nidm_esc(SPM.xCon(xSPM.Ic(c)).name),'xsd:string'},... + 'prov:label',{['Contrast: ' nidm_esc(SPM.xCon(xSPM.Ic(c)).name)],'xsd:string'},... 'prov:value',{SPM.xCon(xSPM.Ic(c)).c','xsd:string'},... }); idConEst = getid(['niiri:contrast_estimation_id' postfix],isHumanReadable); p.activity(idConEst,{... - 'prov:type',nidm_conv('nidm:ContrastEstimation'),... + 'prov:type',nidm_conv('nidm_ContrastEstimation',p),... 'prov:label',['Contrast estimation' strrep(postfix,'_',' ')],... }); p.wasAssociatedWith(idConEst, idSoftware); @@ -548,76 +583,74 @@ idSPM{c} = getid(['niiri:statistic_map_id' postfix],isHumanReadable); p.entity(idSPM{c},{... - 'prov:type',nidm_conv('nidm:StatisticMap'),... + 'prov:type',nidm_conv('nidm_StatisticMap',p),... 'prov:location',{uri(spm_file(files.spm{c},'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.spm{c},'filename'),'xsd:string'},... 'dct:format',niifmt,... - 'prov:label',{['Statistic Map: ' SPM.xCon(xSPM.Ic(c)).name],'xsd:string'},... %esc - nidm_conv('nidm:statisticType'),nidm_conv(['stato:' xSPM.STAT 'Statistic']),... - nidm_conv('nidm:contrastName'),{SPM.xCon(xSPM.Ic(c)).name,'xsd:string'},... %esc - nidm_conv('nidm:errorDegreesOfFreedom'),{xSPM.df(2),'xsd:float'},... - nidm_conv('nidm:effectDegreesOfFreedom'),{xSPM.df(1),'xsd:float'},... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + 'prov:label',{['Statistic Map: ' nidm_esc(SPM.xCon(xSPM.Ic(c)).name)],'xsd:string'},... + nidm_conv('nidm_statisticType',p),nidm_conv(['stato_' xSPM.STAT 'Statistic'],p),... + nidm_conv('nidm_contrastName',p),{nidm_esc(SPM.xCon(xSPM.Ic(c)).name),'xsd:string'},... + nidm_conv('nidm_errorDegreesOfFreedom',p),{xSPM.df(2),'xsd:float'},... + nidm_conv('nidm_effectDegreesOfFreedom',p),{xSPM.df(1),'xsd:float'},... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... 'crypto:sha512',{sha512sum(files.spm{c}),'xsd:string'},... }); - id = originalfile(p,SPM.xCon(xSPM.Ic(c)).Vspm.fname,idSPM{c},nidm_conv('nidm:StatisticMap')); + id = originalfile(p,fullfile(SPM.swd,SPM.xCon(xSPM.Ic(c)).Vspm.fname),idSPM{c},nidm_conv('nidm_StatisticMap',p)); p.wasDerivedFrom(idSPM{c},id); p.wasGeneratedBy(idSPM{c},idConEst); if xSPM.STAT == 'T' idContrast = getid(['niiri:contrast_map_id' postfix],isHumanReadable); p.entity(idContrast,{... - 'prov:type',nidm_conv('nidm:ContrastMap'),... + 'prov:type',nidm_conv('nidm_ContrastMap',p),... 'prov:location',{uri(spm_file(files.con{c},'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.con{c},'filename'),'xsd:string'},... 'dct:format',niifmt,... - 'prov:label',{['Contrast Map: ' SPM.xCon(xSPM.Ic(c)).name],'xsd:string'},... %esc - nidm_conv('nidm:contrastName'),{SPM.xCon(xSPM.Ic(c)).name,'xsd:string'},... %esc - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + 'prov:label',{['Contrast Map: ' nidm_esc(SPM.xCon(xSPM.Ic(c)).name)],'xsd:string'},... + nidm_conv('nidm_contrastName',p),{nidm_esc(SPM.xCon(xSPM.Ic(c)).name),'xsd:string'},... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... 'crypto:sha512',{sha512sum(spm_file(files.con{c},'cpath')),'xsd:string'},... }); - id = originalfile(p,SPM.xCon(xSPM.Ic(c)).Vcon.fname,idContrast,nidm_conv('nidm:ContrastMap')); + id = originalfile(p,fullfile(SPM.swd,SPM.xCon(xSPM.Ic(c)).Vcon.fname),idContrast,nidm_conv('nidm_ContrastMap',p)); p.wasDerivedFrom(idContrast,id); p.wasGeneratedBy(idContrast,idConEst); idSE = getid(['niiri:contrast_standard_error_map_id' postfix],isHumanReadable); p.entity(idSE,{... - 'prov:type',nidm_conv('nidm:ContrastStandardErrorMap'),... + 'prov:type',nidm_conv('nidm_ContrastStandardErrorMap',p),... 'prov:location',{uri(spm_file(files.conse{c},'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.conse{c},'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Contrast Standard Error Map','xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... 'crypto:sha512',{sha512sum(spm_file(files.conse{c},'cpath')),'xsd:string'},... }); p.wasGeneratedBy(idSE,idConEst); end if xSPM.STAT == 'F' - idESS = getid(['niiri:contrast_extra_sum_of_squares_id' postfix],isHumanReadable); - p.entity(idESS,{... - 'prov:type','nidm:ContrastExtraSumOfSquaresMap',... - 'prov:location',{uri(spm_file(files.ess{c},'cpath')),'xsd:anyURI'},... - 'nfo:fileName',{spm_file(files.ess{c},'filename'),'xsd:string'},... + idEffMS = getid(['niiri:contrast_explained_mean_square_map_id' postfix],isHumanReadable); + p.entity(idEffMS,{... + 'prov:type',nidm_conv('nidm_ContrastExplainedMeanSquareMap',p),... + 'prov:location',{uri(spm_file(files.effms{c},'cpath')),'xsd:anyURI'},... + 'nfo:fileName',{spm_file(files.effms{c},'filename'),'xsd:string'},... 'dct:format',niifmt,... - 'prov:label',{'Contrast Extra Sum of Squares Map','xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... - 'crypto:sha512',{sha512sum(spm_file(files.ess{c},'cpath')),'xsd:string'},... + 'prov:label',{'Contrast Explained Mean Square Map','xsd:string'},... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... + 'crypto:sha512',{sha512sum(spm_file(files.effms{c},'cpath')),'xsd:string'},... }); - id = originalfile(p,SPM.xCon(xSPM.Ic(c)).Vcon.fname,idESS,'nidm:ContrastExtraSumOfSquaresMap'); - p.wasDerivedFrom(idESS,id); - p.wasGeneratedBy(idESS,idConEst); + p.wasGeneratedBy(idEffMS,idConEst); end end %-Entity: Height Threshold %-------------------------------------------------------------------------- -thresh(1).type = nidm_conv('obo:Statistic'); +thresh(1).type = nidm_conv('obo_Statistic',p); thresh(1).label = 'Height Threshold'; thresh(1).value = xSPM.u; % TabDat.ftr{1,2}(1) -thresh(2).type = nidm_conv('nidm:pValueUncorrectedClass'); +thresh(2).type = nidm_conv('nidm_PValueUncorrected',p); thresh(2).label = 'Height Threshold'; thresh(2).value = TabDat.ftr{1,2}(2); -thresh(3).type = nidm_conv('obo:pValueFWER'); +thresh(3).type = nidm_conv('obo_pValueFWER',p); thresh(3).label = 'Height Threshold'; thresh(3).value = TabDat.ftr{1,2}(3); td = regexp(xSPM.thresDesc,'p\D?(?[\.\d]+) \((?\S+)\)','names'); @@ -640,9 +673,11 @@ thresh_order = [2 1 3]; % uncorrected thresh_desc = sprintf(': p<%f (unc.)',TabDat.ftr{1,2}(2)); case 'FDR' - warning('FDR not handled.'); - thresh_order = 1:3; % unknown - thresh_desc = ''; + thresh(3).type = nidm_conv('obo_qValueFDR',p); + thresh(3).label = 'Height Threshold'; + thresh(3).value = str2double(td.u); + thresh_order = [3 1 2]; % FDR + thresh_desc = sprintf(': p<%s (FDR)',td.u); otherwise warning('Unkwnown threshold type.'); thresh_order = 1:3; % unknown @@ -655,23 +690,24 @@ idHeightThresh2 = getid('niiri:height_threshold_id_2',isHumanReadable); idHeightThresh3 = getid('niiri:height_threshold_id_3',isHumanReadable); p.entity(idHeightThresh,{... - 'prov:type',nidm_conv('nidm:HeightThreshold'),... - 'prov:label',{thresh(1).label,'xsd:string'},... %esc + 'prov:type',nidm_conv('nidm_HeightThreshold',p),... + 'prov:type',thresh(1).type,... + 'prov:label',{nidm_esc(thresh(1).label),'xsd:string'},... 'prov:value',{thresh(1).value,'xsd:float'},... - nidm_conv('nidm:equivalentThreshold'),idHeightThresh2,... - nidm_conv('nidm:equivalentThreshold'),idHeightThresh3,... + nidm_conv('nidm_equivalentThreshold',p),idHeightThresh2,... + nidm_conv('nidm_equivalentThreshold',p),idHeightThresh3,... }); p.entity(idHeightThresh2,{... - 'prov:type',nidm_conv('nidm:HeightThreshold'),... + 'prov:type',nidm_conv('nidm_HeightThreshold',p),... 'prov:type',thresh(2).type,... - 'prov:label',{thresh(2).label,'xsd:string'},... %esc + 'prov:label',{nidm_esc(thresh(2).label),'xsd:string'},... 'prov:value',{thresh(2).value,'xsd:float'},... }); p.entity(idHeightThresh3,{... - 'prov:type',nidm_conv('nidm:HeightThreshold'),... + 'prov:type',nidm_conv('nidm_HeightThreshold',p),... 'prov:type',thresh(3).type,... - 'prov:label',{thresh(3).label,'xsd:string'},... %esc + 'prov:label',{nidm_esc(thresh(3).label),'xsd:string'},... 'prov:value',{thresh(3).value,'xsd:float'},... }); @@ -691,23 +727,23 @@ idExtentThresh2 = getid('niiri:extent_threshold_id_2',isHumanReadable); idExtentThresh3 = getid('niiri:extent_threshold_id_3',isHumanReadable); p.entity(idExtentThresh,{... - 'prov:type',nidm_conv('nidm:ExtentThreshold'),... - 'prov:type',nidm_conv('obo:Statistic'),... - 'prov:label',{['Extent Threshold: k=' num2str(TabDat.ftr{2,2}(1))],'xsd:string'},... - nidm_conv('nidm:clusterSizeInVoxels'),{TabDat.ftr{2,2}(1),'xsd:int'},... % xSPM.k - nidm_conv('nidm:clusterSizeInResels'),{TabDat.ftr{2,2}(1)*V2R,'xsd:float'},... - nidm_conv('nidm:equivalentThreshold'),idExtentThresh2,... - nidm_conv('nidm:equivalentThreshold'),idExtentThresh3,... + 'prov:type',nidm_conv('nidm_ExtentThreshold',p),... + 'prov:type',nidm_conv('obo_Statistic',p),... + 'prov:label',{['Extent Threshold: k>=' num2str(TabDat.ftr{2,2}(1))],'xsd:string'},... + nidm_conv('nidm_clusterSizeInVoxels',p),{TabDat.ftr{2,2}(1),'xsd:int'},... % xSPM.k + nidm_conv('nidm_clusterSizeInResels',p),{TabDat.ftr{2,2}(1)*V2R,'xsd:float'},... + nidm_conv('nidm_equivalentThreshold',p),idExtentThresh2,... + nidm_conv('nidm_equivalentThreshold',p),idExtentThresh3,... }); p.entity(idExtentThresh2,{... - 'prov:type',nidm_conv('nidm:ExtentThreshold'),... - 'prov:type',nidm_conv('obo:pValueFWER'),... + 'prov:type',nidm_conv('nidm_ExtentThreshold',p),... + 'prov:type',nidm_conv('obo_pValueFWER',p),... 'prov:label',{'Extent Threshold','xsd:string'},... 'prov:value',{kk(2),'xsd:float'},... }); p.entity(idExtentThresh3,{... - 'prov:type',nidm_conv('nidm:ExtentThreshold'),... - 'prov:type',nidm_conv('nidm:pValueUncorrectedClass'),... + 'prov:type',nidm_conv('nidm_ExtentThreshold',p),... + 'prov:type',nidm_conv('nidm_PValueUncorrected',p),... 'prov:label',{'Extent Threshold','xsd:string'},... 'prov:value',{kk(1),'xsd:float'},... }); @@ -720,10 +756,10 @@ % clusterConnectivityCriterion = 18; idPeakDefCrit = getid('niiri:peak_definition_criteria_id',isHumanReadable); p.entity(idPeakDefCrit,{... - 'prov:type',nidm_conv('nidm:PeakDefinitionCriteria'),... + 'prov:type',nidm_conv('nidm_PeakDefinitionCriteria',p),... 'prov:label',{'Peak Definition Criteria','xsd:string'},... - nidm_conv('nidm:maxNumberOfPeaksPerCluster'),{maxNumberOfPeaksPerCluster,'xsd:int'},... - nidm_conv('nidm:minDistanceBetweenPeaks'),{minDistanceBetweenPeaks,'xsd:float'},... + nidm_conv('nidm_maxNumberOfPeaksPerCluster',p),{maxNumberOfPeaksPerCluster,'xsd:int'},... + nidm_conv('nidm_minDistanceBetweenPeaks',p),{minDistanceBetweenPeaks,'xsd:float'},... }); %-Entity: Cluster Definition Criteria @@ -731,26 +767,28 @@ clusterConnectivityCriterion = 18; % see spm_max.m idClusterDefCrit = getid('niiri:cluster_definition_criteria_id',isHumanReadable); p.entity(idClusterDefCrit,{... - 'prov:type',nidm_conv('nidm:ClusterDefinitionCriteria'),... + 'prov:type',nidm_conv('nidm_ClusterDefinitionCriteria',p),... 'prov:label',{sprintf('Cluster Connectivity Criterion: %d',clusterConnectivityCriterion),'xsd:string'},... - nidm_conv('nidm:hasConnectivityCriterion'),nidm_conv(sprintf('nidm:voxel%dconnected',clusterConnectivityCriterion)),... + nidm_conv('nidm_hasConnectivityCriterion',p),nidm_conv(sprintf('nidm_voxel%dconnected',clusterConnectivityCriterion),p),... }); %-Activity: Inference %========================================================================== if numel(xSPM.Ic) == 1 - st = {'prov:type',nidm_conv('nidm:Inference'), ... - nidm_conv('nidm:hasAlternativeHypothesis'),nidm_conv('nidm:OneTailedTest'),... + st = {'prov:type',nidm_conv('nidm_Inference',p), ... + nidm_conv('nidm_hasAlternativeHypothesis',p),nidm_conv('nidm_OneTailedTest',p),... 'prov:label','Inference'}; else if xSPM.n == 1 - st = {'prov:type',nidm_conv('nidm:ConjunctionInference'), ... + st = {'prov:type',nidm_conv('nidm_ConjunctionInference',p), ... + nidm_conv('nidm_hasAlternativeHypothesis',p),nidm_conv('nidm_OneTailedTest',p),... 'prov:label','Conjunction Inference'}; else - st = {'prov:type','nidm:kConjunctionInference', ... - 'prov:label','k-Conjunction Inference', ... - nidm_conv('spm:partialConjunctionDegree'),xSPM.n}; + st = {'prov:type',nidm_conv('spm_PartialConjunctionInference',p), ... + nidm_conv('nidm_hasAlternativeHypothesis',p),nidm_conv('nidm_OneTailedTest',p),... + 'prov:label','Partial Conjunction Inference', ... + nidm_conv('spm_partialConjunctionDegree',p),{xSPM.n,'xsd:int'}}; end end idInference = getid('niiri:inference_id',isHumanReadable); @@ -774,7 +812,7 @@ V = spm_vol(files.dmask{i}); if ~spm_check_orientations(struct('dim',{xSPM.DIM',V.dim},... 'mat',{xSPM.M,V.mat}),false) - currCoordSpace = coordspace(p,V.mat,V.dim',xSPM.units,coordsys); + currCoordSpace = coordspace(p,V.mat,V.dim',units,coordsys); else currCoordSpace = id_data_coordspace; end @@ -783,15 +821,15 @@ else postfix = sprintf('_%d',i); end idDMask = getid(['niiri:display_mask_map_id' postfix],isHumanReadable); p.entity(idDMask,{... - 'prov:type',nidm_conv('nidm:DisplayMaskMap'),... + 'prov:type',nidm_conv('nidm_DisplayMaskMap',p),... 'prov:location',{uri(spm_file(files.dmask{i},'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.dmask{i},'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Display Mask Map','xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),currCoordSpace,... + nidm_conv('nidm_inCoordinateSpace',p),currCoordSpace,... 'crypto:sha512',{sha512sum(spm_file(files.dmask{i},'cpath')),'xsd:string'},... }); - id = originalfile(p,files.dmask{i},idDMask,nidm_conv('nidm:DisplayMaskMap')); + id = originalfile(p,files.dmask{i},idDMask,nidm_conv('nidm_DisplayMaskMap',p)); p.wasDerivedFrom(idDMask,id); p.used(idInference, idDMask); end @@ -802,7 +840,7 @@ V = spm_vol(files.svcmask); if ~spm_check_orientations(struct('dim',{xSPM.DIM',V.dim},... 'mat',{xSPM.M,V.mat}),false) - currCoordSpace = coordspace(p,V.mat,V.dim',xSPM.units,coordsys); + currCoordSpace = coordspace(p,V.mat,V.dim',units,coordsys); else currCoordSpace = id_data_coordspace; end @@ -812,7 +850,7 @@ 'prov:location',{uri(spm_file(files.svcmask,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.svcmask,'filename'),'xsd:string'},... 'prov:label',{'Sub-volume','xsd:string'},... - nidm_conv('nidm:inCoordinateSpace'),currCoordSpace,... + nidm_conv('nidm_inCoordinateSpace',p),currCoordSpace,... 'crypto:sha512',{sha512sum(spm_file(files.svcmask,'cpath')),'xsd:string'},... }); id = originalfile(p,files.svcmask,idSVC,'nidm:SubVolumeMap'); @@ -825,29 +863,38 @@ if spm_get_defaults('stats.rft.nonstat'), rftstat = {'false','xsd:boolean'}; else rftstat = {'true','xsd:boolean'}; end idSearchSpace = getid('niiri:search_space_id',isHumanReadable); -p.entity(idSearchSpace,{... - 'prov:type',nidm_conv('nidm:SearchSpaceMaskMap'),... +search_space_attributes = {... + 'prov:type',nidm_conv('nidm_SearchSpaceMaskMap',p),... 'prov:location',{uri(spm_file(files.searchspace,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.searchspace,'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Search Space Mask Map','xsd:string'}... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... - nidm_conv('nidm:searchVolumeInVoxels'),{xSPM.S,'xsd:int'},... - nidm_conv('nidm:searchVolumeInUnits'),{TabDat.ftr{8,2}(1),'xsd:float'},... - nidm_conv('nidm:reselSizeInVoxels'),{TabDat.ftr{9,2}(end),'xsd:float'},... - nidm_conv('nidm:searchVolumeInResels'),{TabDat.ftr{8,2}(3),'xsd:float'},... - nidm_conv('nidm:searchVolumeReselsGeometry'),{xSPM.R,'xsd:string'},... - nidm_conv('nidm:noiseFWHMInVoxels'),{xSPM.FWHM,'xsd:string'},... - nidm_conv('nidm:noiseFWHMInUnits'),{TabDat.ftr{7,2}(1:3),'xsd:string'},... - nidm_conv('nidm:randomFieldStationarity'),rftstat,... - nidm_conv('nidm:expectedNumberOfVoxelsPerCluster'),{TabDat.ftr{3,2},'xsd:float'},... - nidm_conv('nidm:expectedNumberOfClusters'),{TabDat.ftr{4,2},'xsd:float'},... - nidm_conv('spm:heightCriticalThresholdFWE05'),{xSPM.uc(1),'xsd:float'},... - nidm_conv('spm:heightCriticalThresholdFDR05'),{xSPM.uc(2),'xsd:float'},... - nidm_conv('spm:smallestSignifClusterSizeInVoxelsFWE05'),{xSPM.uc(3),'xsd:int'},... - nidm_conv('spm:smallestSignifClusterSizeInVoxelsFDR05'),{xSPM.uc(4),'xsd:int'},... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... + nidm_conv('nidm_searchVolumeInVoxels',p),{xSPM.S,'xsd:int'},... + nidm_conv('nidm_searchVolumeInUnits',p),{TabDat.ftr{8,2}(1),'xsd:float'},... + nidm_conv('nidm_reselSizeInVoxels',p),{TabDat.ftr{9,2}(end),'xsd:float'},... + nidm_conv('nidm_searchVolumeInResels',p),{TabDat.ftr{8,2}(3),'xsd:float'},... + nidm_conv('spm_searchVolumeReselsGeometry',p),{xSPM.R,'xsd:string'},... + nidm_conv('nidm_noiseFWHMInVoxels',p),{xSPM.FWHM,'xsd:string'},... + nidm_conv('nidm_noiseFWHMInUnits',p),{TabDat.ftr{7,2}(1:3),'xsd:string'},... + nidm_conv('nidm_randomFieldStationarity',p),rftstat,... + nidm_conv('nidm_expectedNumberOfVoxelsPerCluster',p),{TabDat.ftr{3,2},'xsd:float'},... + nidm_conv('nidm_expectedNumberOfClusters',p),{TabDat.ftr{4,2},'xsd:float'},... + nidm_conv('nidm_heightCriticalThresholdFWE05',p),{xSPM.uc(1),'xsd:float'},... + nidm_conv('nidm_heightCriticalThresholdFDR05',p),{xSPM.uc(2),'xsd:float'},... 'crypto:sha512',{sha512sum(spm_file(files.searchspace,'cpath')),'xsd:string'},... - }); + }; +if isfinite(xSPM.uc(3)) + search_space_attributes = [search_space_attributes, {... + nidm_conv('spm_smallestSupraThresholdClusterSizeInVoxelsFWE05',p),{xSPM.uc(3),'xsd:int'},... + }]; +end +if isfinite(xSPM.uc(4)) + search_space_attributes = [search_space_attributes, {... + nidm_conv('spm_smallestSupraThresholdClusterSizeInVoxelsFDR05',p),{xSPM.uc(4),'xsd:int'},... + }]; +end +p.entity(idSearchSpace,search_space_attributes); p.wasGeneratedBy(idSearchSpace, idInference); %-Entity: Excursion Set @@ -863,20 +910,20 @@ idClusterLabelsMap = getid('niiri:cluster_label_map_id',isHumanReadable); idMaximumIntensityProjection = getid('niiri:maximum_intensity_projection_id',isHumanReadable); p.entity(idExcursionSet,{... - 'prov:type',nidm_conv('nidm:ExcursionSetMap'),... + 'prov:type',nidm_conv('nidm_ExcursionSetMap',p),... 'prov:location',{uri(spm_file(files.tspm,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.tspm,'filename'),'xsd:string'},... 'dct:format',niifmt,... 'prov:label',{'Excursion Set Map','xsd:string'},... - nidm_conv('nidm:numberOfClusters'),{c,'xsd:int'},... - nidm_conv('nidm:pValue'),{pc,'xsd:float'},... - nidm_conv('nidm:hasClusterLabelsMap'),idClusterLabelsMap,... - nidm_conv('nidm:hasMaximumIntensityProjection'),idMaximumIntensityProjection,... - nidm_conv('nidm:inCoordinateSpace'),id_data_coordspace,... + nidm_conv('nidm_numberOfSupraThresholdClusters',p),{c,'xsd:int'},... + nidm_conv('nidm_pValue',p),{pc,'xsd:float'},... + nidm_conv('nidm_hasClusterLabelsMap',p),idClusterLabelsMap,... + nidm_conv('nidm_hasMaximumIntensityProjection',p),idMaximumIntensityProjection,... + nidm_conv('nidm_inCoordinateSpace',p),id_data_coordspace,... 'crypto:sha512',{sha512sum(spm_file(files.tspm,'cpath')),'xsd:string'},... }); p.entity(idClusterLabelsMap,{... - 'prov:type',nidm_conv('nidm:ClusterLabelsMap'),... + 'prov:type',nidm_conv('nidm_ClusterLabelsMap',p),... 'prov:location',{uri(spm_file(files.clust,'cpath')),'xsd:anyURI'},... 'nfo:fileName',{spm_file(files.clust,'filename'),'xsd:string'},... 'dct:format',niifmt,... @@ -895,16 +942,16 @@ idCluster = cell(1,numel(idx)); for i=1:numel(idx) iClus = sprintf('%04d',i); - idCluster{i} = getid(['niiri:significant_cluster_' iClus],isHumanReadable); + idCluster{i} = getid(['niiri:supra_threshold_cluster_' iClus],isHumanReadable); p.entity(idCluster{i},{... - 'prov:type',nidm_conv('nidm:SignificantCluster'),... - 'prov:label',{['Significant Cluster: ' iClus],'xsd:string'},... - nidm_conv('nidm:clusterSizeInVoxels'),{TabDat.dat{idx(i),5},'xsd:int'},... - nidm_conv('nidm:clusterSizeInResels'),{TabDat.dat{idx(i),5}*V2R,'xsd:float'},... - nidm_conv('nidm:pValueUncorrected'),{TabDat.dat{idx(i),6},'xsd:float'},... - nidm_conv('nidm:pValueFWER'),{TabDat.dat{idx(i),3},'xsd:float'},... - nidm_conv('nidm:qValueFDR'),{TabDat.dat{idx(i),4},'xsd:float'},... - nidm_conv('nidm:clusterLabelId'),{num2str(i),'xsd:int'},... + 'prov:type',nidm_conv('nidm_SupraThresholdCluster',p),... + 'prov:label',{['Supra-Threshold Cluster: ' iClus],'xsd:string'},... + nidm_conv('nidm_clusterSizeInVoxels',p),{TabDat.dat{idx(i),5},'xsd:int'},... + nidm_conv('nidm_clusterSizeInResels',p),{TabDat.dat{idx(i),5}*V2R,'xsd:float'},... + nidm_conv('nidm_pValueUncorrected',p),{TabDat.dat{idx(i),6},'xsd:float'},... + nidm_conv('nidm_pValueFWER',p),{TabDat.dat{idx(i),3},'xsd:float'},... + nidm_conv('nidm_qValueFDR',p),{TabDat.dat{idx(i),4},'xsd:float'},... + nidm_conv('nidm_clusterLabelId',p),{num2str(i),'xsd:int'},... }); p.wasDerivedFrom(idCluster{i}, idExcursionSet); end @@ -918,21 +965,21 @@ idCoordinate = getid(['niiri:coordinate_' iPeak],isHumanReadable); p.entity(idPeak,{... - 'prov:type',nidm_conv('nidm:Peak'),... + 'prov:type',nidm_conv('nidm_Peak',p),... 'prov:label',{['Peak: ' iPeak],'xsd:string'},... 'prov:location',idCoordinate,... 'prov:value',{TabDat.dat{i,9},'xsd:float'},... - nidm_conv('nidm:equivalentZStatistic'),{xsdfloat(TabDat.dat{i,10}),'xsd:float'},... - nidm_conv('nidm:pValueUncorrected'),{TabDat.dat{i,11},'xsd:float'},... - nidm_conv('nidm:pValueFWER'),{TabDat.dat{i,7},'xsd:float'},... - nidm_conv('nidm:qValueFDR'),{TabDat.dat{i,8},'xsd:float'},... + nidm_conv('nidm_equivalentZStatistic',p),{xsdfloat(TabDat.dat{i,10}),'xsd:float'},... + nidm_conv('nidm_pValueUncorrected',p),{TabDat.dat{i,11},'xsd:float'},... + nidm_conv('nidm_pValueFWER',p),{TabDat.dat{i,7},'xsd:float'},... + nidm_conv('nidm_qValueFDR',p),{TabDat.dat{i,8},'xsd:float'},... }); p.entity(idCoordinate,{... 'prov:type','prov:Location',... - 'prov:type',nidm_conv('nidm:Coordinate'),... + 'prov:type',nidm_conv('nidm_Coordinate',p),... 'prov:label',{['Coordinate: ' iPeak],'xsd:string'},... - nidm_conv('nidm:coordinateVector'),{TabDat.dat{i,12}(1:3),'xsd:string'},... + nidm_conv('nidm_coordinateVector',p),{TabDat.dat{i,12}(1:3),'xsd:string'},... }); p.wasDerivedFrom(idPeak, idCluster{idx(i)}); @@ -947,7 +994,17 @@ serialize(pp,fullfile(outdir,'nidm.ttl')); %serialize(pp,fullfile(outdir,'nidm.json')); %serialize(pp,fullfile(outdir,'nidm.pdf')); -%zip(fullfile(SPM.swd,[spm_file(outdir,'basename'),'.nidm.zip']),outdir) + +i = 1; +while true + nidmfile = fullfile(SPM.swd,sprintf('spm_%04d.nidm.zip',i)); + if spm_existfile(nidmfile), i = i + 1; else break; end +end +f = zip(nidmfile,'*',outdir); +for i=1:numel(f) + spm_unlink(fullfile(outdir,f{i})); +end +rmdir(outdir); prov = pp; @@ -963,9 +1020,9 @@ %========================================================================== -% function str = esc(str) +% function str = html_esc(str) %========================================================================== -function str = esc(str) +function str = html_esc(str) %-Escape % See http://www.w3.org/TR/html4/charset.html#h-5.3.2 str = strrep(str,'&','&'); @@ -979,20 +1036,19 @@ %========================================================================== function u = uri(u) %-File URI scheme -if ispc, s='/'; else s=''; end -u = ['file://' s strrep(spm_file(u,'cpath'),'\','/')]; +%if ispc, s='/'; else s=''; end +%u = ['file://' s strrep(spm_file(u,'cpath'),'\','/')]; e = ' '; for i=1:length(e) u = strrep(u,e(i),['%' dec2hex(e(i))]); end -u = ['file://./' spm_file(u,'filename')]; +u = spm_file(u,'filename'); %========================================================================== % function checksum = sha512sum(file) %========================================================================== function checksum = sha512sum(file) -%checksum = 'e43b6e01b0463fe7d40782137867a...'; return % TEMP (short) md = java.security.MessageDigest.getInstance('SHA-512'); file = spm_file(file,'cpath'); fid = fopen(file,'rb'); @@ -1007,7 +1063,6 @@ % function checksum = md5sum(data) %========================================================================== function checksum = md5sum(data) -%checksum = 'd41d8cd98f00b204e9800998ecf8427e'; return % TEMP (short) if ~nargin data = char(java.util.UUID.randomUUID); end @@ -1108,14 +1163,14 @@ function make_ROI(fname,DIM,M,xY) M = M(1:3,1:3); id = getid(['niiri:coordinate_space_id_' num2str(index)],isHumanReadable); p.entity(id,{... - 'prov:type',nidm_conv('nidm:CoordinateSpace'),... + 'prov:type',nidm_conv('nidm_CoordinateSpace',p),... 'prov:label',{['Coordinate space ' num2str(index)],'xsd:string'},... - nidm_conv('nidm:voxelToWorldMapping'),{v2wm,'xsd:string'},... - nidm_conv('nidm:voxelUnits'),{units,'xsd:string'},... - nidm_conv('nidm:voxelSize'),{sqrt(diag(M'*M))','xsd:string'},... - nidm_conv('nidm:inWorldCoordinateSystem'),coordsys,... - nidm_conv('nidm:numberOfDimensions'),{numel(DIM),'xsd:int'},... - nidm_conv('nidm:dimensionsInVoxels'),{DIM,'xsd:string'} + nidm_conv('nidm_voxelToWorldMapping',p),{v2wm,'xsd:string'},... + nidm_conv('nidm_voxelUnits',p),{units,'xsd:string'},... + nidm_conv('nidm_voxelSize',p),{sqrt(diag(M'*M))','xsd:string'},... + nidm_conv('nidm_inWorldCoordinateSystem',p),coordsys,... + nidm_conv('nidm_numberOfDimensions',p),{numel(DIM),'xsd:int'},... + nidm_conv('nidm_dimensionsInVoxels',p),{DIM,'xsd:string'} }); %========================================================================== @@ -1140,6 +1195,7 @@ function make_ROI(fname,DIM,M,xY) else id = md5sum(checksum); end + id = ['niiri:' id]; end %========================================================================== @@ -1152,208 +1208,272 @@ function make_ROI(fname,DIM,M,xY) i = isHR; %========================================================================== -% function +% function out = nidm_conv(in,p) %========================================================================== -function out = nidm_conv(in) +function out = nidm_conv(in,p) persistent C if isempty(C), C = nidm_constants; end -i = find(ismember(C(:,1),in)); +i = find(ismember(C(:,2),in)); if ~isempty(i) - out = C{i,2}; + out = [C{i,2} ':']; + if nargin == 2 + prefix = ''; + qname = C{i,1}; + j = find(qname == ':'); + if ~isempty(j) + prefix = qname(1:j(end)-1); + qname = qname(j(end)+1:end); + end + % should instead use ns = p.get_namespace; + switch prefix + case 'nidm' + url = 'http://purl.org/nidash/nidm#'; + case 'spm' + url = 'http://purl.org/nidash/spm#'; + case 'obo' + url = 'http://purl.obolibrary.org/obo/'; + case 'neurolex' + url = 'http://neurolex.org/wiki/'; + otherwise + warning('Problem with prefix.'); + url = ''; + end + p.add_namespace(C{i,2},[url qname]); + end else warning('Unknown element ''%s''.',in); out = in; end +%========================================================================== +% function S = nidm_esc(S) +%========================================================================== +function S = nidm_esc(S) +S = strrep(S, sprintf('\n'), '\n'); + +%========================================================================== +% function nidm_store_constants +%========================================================================== +function nidm_store_constants +urlwrite('https://raw.githubusercontent.com/incf-nidash/nidm/master/nidm/nidm-results/terms/prefixes.csv','prefixes.csv'); +C = reshape(textread('prefixes.csv','%s','delimiter',',','headerlines',1),2,[])'; +fprintf('C = {...\n'); +for i=1:size(C,1) + fprintf('''%s'', ''%s'';...\n',C{i,1},strrep(C{i,2},':','')); +end +fprintf('};\n'); + %========================================================================== % function C = nidm_constants %========================================================================== function C = nidm_constants -% from https://github.com/incf-nidash/nidm/blob/master/scripts/Constants.py +% automatically generated by nidm_store_constants C = {... -'neurolex:SPM', 'neurolex:nif-0000-00343';... -'spm:partialConjunctionDegree','spm:SPM_0000015';... -'spm:smallestSignifClusterSizeInVoxelsFWE05', 'spm:SPM_0000014';... -'spm:smallestSignifClusterSizeInVoxelsFDR05', 'spm:SPM_0000013';... -'spm:smallestSignifClusterSizeInVerticessFWE05', 'spm:SPM_0000012';... -'spm:smallestSignifClusterSizeInVerticesFDR05', 'spm:SPM_0000011';... -'nidm:searchVolumeReselsGeometry', 'spm:SPM_0000010';... -'spm:TemporalDerivative', 'spm:SPM_0000006';... -'SPM_KCONJUNCTION_INFERENCE', 'spm:SPM_0000005';... -'spm:GammaDifferenceHRF', 'spm:SPM_0000004';... -'spm:DispersionDerivative', 'spm:SPM_0000003';... -'spm:DCTDriftModel', 'spm:SPM_0000002';... -'spm:driftCutoffPeriod', 'spm:SPM_0000001';... -'NIDM_CONTRAST_EXPLAINED_MEAN_SQUARE_MAP','nidm:NIDM_0000163';... -'NIDM_THRESHOLD','nidm:NIDM_0000162';... -'nidm:equivalentThreshold','nidm:NIDM_0000161';... -'nidm:pValueUncorrectedClass','nidm:NIDM_0000160';... -'nidm:noiseFWHMInVoxels', 'nidm:NIDM_0000159';... -'nidm:noiseFWHMInVertices', 'nidm:NIDM_0000158';... -'nidm:noiseFWHMInUnits', 'nidm:NIDM_0000157';... -'nidm:clusterSizeInResels','nidm:NIDM_0000156';... -'nidm:fMRIDesign','nidm:NIDM_0000155';... -'NIDM_MIXED_DESIGN','nidm:NIDM_0000154';... -'NIDM_EVENT_RELATED_DESIGN','nidm:NIDM_0000153';... -'nidm:BlockDesign','nidm:NIDM_0000152';... -'NIDM_SINE_BASIS_SET','nidm:NIDM_0000151';... -'NIDM_LINEAR_SPLINE_BASIS_SET','nidm:NIDM_0000150';... -'nidm:searchVolumeInResels','nidm:NIDM_0000149';... -'nidm:reselSizeInVoxels','nidm:NIDM_0000148';... -'spm:heightCriticalThresholdFWE05','nidm:NIDM_0000147';... -'spm:heightCriticalThresholdFDR05','nidm:NIDM_0000146';... -'NIDM_NOISE_ROUGHNESS_IN_VOXELS','nidm:NIDM_0000145';... -'nidm:ReselsPerVoxelMap','nidm:NIDM_0000144';... -'nidm:expectedNumberOfVoxelsPerCluster','nidm:NIDM_0000143';... -'nidm:expectedNumberOfVerticesPerCluster','nidm:NIDM_0000142';... -'nidm:expectedNumberOfClusters','nidm:NIDM_0000141';... -'NIDM_CLUSTER_CENTER_OF_GRAVITY','nidm:NIDM_0000140';... -'NIDM_COORDINATE_VECTOR_IN_VOXELS','nidm:NIDM_0000139';... -'nidm:hasMaximumIntensityProjection','nidm:NIDM_0000138';... -'nidm:searchVolumeInVertices','nidm:NIDM_0000137';... -'nidm:searchVolumeInUnits','nidm:NIDM_0000136';... -'NIDM_CONTRAST_VARIANCE_MAP','nidm:NIDM_0000135';... -'nidm:withEstimationMethod','nidm:NIDM_0000134';... -'nidm:voxelUnits','nidm:NIDM_0000133';... -'nidm:voxelToWorldMapping','nidm:NIDM_0000132';... -'nidm:voxelSize','nidm:NIDM_0000131';... -'nidm:voxel6connected','nidm:NIDM_0000130';... -'nidm:voxel26connected','nidm:NIDM_0000129';... -'nidm:voxel18connected','nidm:NIDM_0000128';... -'nidm:version','nidm:NIDM_0000127';... -'nidm:varianceSpatialModel','nidm:NIDM_0000126';... -'nidm:userSpecifiedThresholdType','nidm:NIDM_0000125';... -'nidm:targetIntensity','nidm:NIDM_0000124';... -'nidm:statisticType','nidm:NIDM_0000123';... -'nidm:softwareVersion','nidm:NIDM_0000122';... -'nidm:searchVolumeInVoxels','nidm:NIDM_0000121';... -'nidm:randomFieldStationarity','nidm:NIDM_0000120';... -'nidm:qValueFDR','nidm:NIDM_0000119';... -'NIDM_PIXEL8CONNECTED','nidm:NIDM_0000118';... -'NIDM_PIXEL4CONNECTED','nidm:NIDM_0000117';... -'nidm:pValueUncorrected','nidm:NIDM_0000116';... -'nidm:pValueFWER','nidm:NIDM_0000115';... -'nidm:pValue','nidm:NIDM_0000114';... -'NIDM_OBJECT_MODEL','nidm:NIDM_0000113';... -'nidm:numberOfDimensions','nidm:NIDM_0000112';... -'nidm:numberOfClusters','nidm:NIDM_0000111';... -'NIDM_GAUSSIAN_HRF','nidm:NIDM_0000110';... -'nidm:minDistanceBetweenPeaks','nidm:NIDM_0000109';... -'nidm:maxNumberOfPeaksPerCluster','nidm:NIDM_0000108';... -'nidm:maskedMedian','nidm:NIDM_0000107';... -'nidm:isUserDefined','nidm:NIDM_0000106';... -'nidm:inWorldCoordinateSystem','nidm:NIDM_0000105';... -'nidm:inCoordinateSpace','nidm:NIDM_0000104';... -'NIDM_HAS_MAP_HEADER','nidm:NIDM_0000103';... -'nidm:hasHRFBasis','nidm:NIDM_0000102';... -'nidm:hasErrorDistribution','nidm:NIDM_0000101';... -'nidm:hasErrorDependence','nidm:NIDM_0000100';... -'nidm:hasConnectivityCriterion','nidm:NIDM_0000099';... -'nidm:hasClusterLabelsMap','nidm:NIDM_0000098';... -'nidm:hasAlternativeHypothesis','nidm:NIDM_0000097';... -'nidm:grandMeanScaling','nidm:NIDM_0000096';... -'nidm:errorVarianceHomogeneous','nidm:NIDM_0000094';... -'nidm:errorDegreesOfFreedom','nidm:NIDM_0000093';... -'nidm:equivalentZStatistic','nidm:NIDM_0000092';... -'nidm:effectDegreesOfFreedom','nidm:NIDM_0000091';... -'nidm:dimensionsInVoxels','nidm:NIDM_0000090';... -'nidm:dependenceSpatialModel','nidm:NIDM_0000089';... -'nidm:hasDriftModel','nidm:NIDM_0000088';... -'NIDM_DRIFT_MODEL','nidm:NIDM_0000087';... -'nidm:coordinateVector','nidm:NIDM_0000086';... -'nidm:contrastName','nidm:NIDM_0000085';... -'nidm:clusterSizeInVoxels','nidm:NIDM_0000084';... -'nidm:clusterSizeInVertices','nidm:NIDM_0000083';... -'nidm:clusterLabelId','nidm:NIDM_0000082';... -'NIDM_WORLD_COORDINATE_SYSTEM','nidm:NIDM_0000081';... -'NIDM_VOXEL_CONNECTIVITY_CRITERION','nidm:NIDM_0000080';... -'NIDM_TWO_TAILED_TEST','nidm:NIDM_0000079';... -'NIDM_TALAIRACH_COORDINATE_SYSTEM','nidm:NIDM_0000078';... -'NIDM_SUBJECT_COORDINATE_SYSTEM','nidm:NIDM_0000077';... -'nidm:StatisticMap','nidm:NIDM_0000076';... -'NIDM_STANDARDIZED_COORDINATE_SYSTEM','nidm:NIDM_0000075';... -'nidm:SpatiallyRegularizedModel','nidm:NIDM_0000074';... -'nidm:SpatiallyLocalModel','nidm:NIDM_0000073';... -'nidm:SpatiallyGlobalModel','nidm:NIDM_0000072';... -'NIDM_SPATIAL_MODEL','nidm:NIDM_0000071';... -'nidm:SignificantCluster','nidm:NIDM_0000070';... -'NIDM_FOURIER_BASIS_SET','nidm:NIDM_0000069';... -'nidm:SearchSpaceMaskMap','nidm:NIDM_0000068';... -'NIDM_CUSTOM_BASIS_SET','nidm:NIDM_0000067';... -'nidm:ResidualMeanSquaresMap','nidm:NIDM_0000066';... -'NIDM_POISSON_DISTRIBUTION','nidm:NIDM_0000065';... -'NIDM_PIXEL_CONNECTIVITY_CRITERION','nidm:NIDM_0000064';... -'nidm:PeakDefinitionCriteria','nidm:NIDM_0000063';... -'nidm:Peak','nidm:NIDM_0000062';... -'nidm:ParameterEstimateMap','nidm:NIDM_0000061';... -'nidm:OneTailedTest','nidm:NIDM_0000060';... -'NIDM_NON_PARAMETRIC_SYMMETRIC_DISTRIBUTION','nidm:NIDM_0000059';... -'NIDM_NON_PARAMETRIC_DISTRIBUTION','nidm:NIDM_0000058';... -'nidm:objectModel','nidm:NIDM_0000057';... -'nidm:ModelParametersEstimation','nidm:NIDM_0000056';... -'NIDM_MNI305_COORDINATE_SYSTEM','nidm:NIDM_0000055';... -'nidm:MaskMap','nidm:NIDM_0000054';... -'NIDM_MAP_HEADER','nidm:NIDM_0000053';... -'NIDM_MAP','nidm:NIDM_0000052';... -'nidm:MNICoordinateSystem','nidm:NIDM_0000051';... -'NIDM_IXI549_COORDINATE_SYSTEM','nidm:NIDM_0000050';... -'nidm:Inference','nidm:NIDM_0000049';... -'nidm:IndependentError','nidm:NIDM_0000048';... -'NIDM_ICBM_MNI152_NON_LINEAR6TH_GENERATION_COORDINATE_SYSTEM','nidm:NIDM_0000047';... -'NIDM_ICBM_MNI152_NON_LINEAR2009C_SYMMETRIC_COORDINATE_SYSTEM','nidm:NIDM_0000046';... -'NIDM_ICBM_MNI152_NON_LINEAR2009C_ASYMMETRIC_COORDINATE_SYSTEM','nidm:NIDM_0000045';... -'NIDM_ICBM_MNI152_NON_LINEAR2009B_SYMMETRIC_COORDINATE_SYSTEM','nidm:NIDM_0000044';... -'NIDM_ICBM_MNI152_NON_LINEAR2009B_ASYMMETRIC_COORDINATE_SYSTEM','nidm:NIDM_0000043';... -'NIDM_ICBM_MNI152_NON_LINEAR2009A_SYMMETRIC_COORDINATE_SYSTEM','nidm:NIDM_0000042';... -'NIDM_ICBM_MNI152_NON_LINEAR2009A_ASYMMETRIC_COORDINATE_SYSTEM','nidm:NIDM_0000041';... -'NIDM_ICBM_MNI152_LINEAR_COORDINATE_SYSTEM','nidm:NIDM_0000040';... -'NIDM_ICBM452_WARP5_COORDINATE_SYSTEM','nidm:NIDM_0000039';... -'NIDM_ICBM452_AIR_COORDINATE_SYSTEM','nidm:NIDM_0000038';... -'NIDM_HEMODYNAMIC_RESPONSE_FUNCTION_DERIVATIVE','nidm:NIDM_0000037';... -'NIDM_HEMODYNAMIC_RESPONSE_FUNCTION_BASIS','nidm:NIDM_0000036';... -'NIDM_HEMODYNAMIC_RESPONSE_FUNCTION','nidm:NIDM_0000035';... -'nidm:HeightThreshold','nidm:NIDM_0000034';... -'nidm:GrandMeanMap','nidm:NIDM_0000033';... -'nidm:GaussianDistribution','nidm:NIDM_0000032';... -'NIDM_GAMMA_HRF','nidm:NIDM_0000031';... -'NIDM_GAMMA_HRB','nidm:NIDM_0000030';... -'NIDM_GAMMA_DIFFERENCE_HRF','nidm:NIDM_0000029';... -'NIDM_FINITE_IMPULSE_RESPONSE_HRB','nidm:NIDM_0000028';... -'nidm:Results','nidm:NIDM_0000027';... -'nidm:ExtentThreshold','nidm:NIDM_0000026';... -'nidm:ExcursionSetMap','nidm:NIDM_0000025';... -'NIDM_EXCHANGEABLE_ERROR','nidm:NIDM_0000024';... -'nidm:ErrorModel','nidm:NIDM_0000023';... -'NIDM_ERROR_DISTRIBUTION','nidm:NIDM_0000022';... -'nidm:regressorNames','nidm:NIDM_0000021';... -'nidm:DisplayMaskMap','nidm:NIDM_0000020';... -'nidm:DesignMatrix','nidm:NIDM_0000019';... -'nidm:Data','nidm:NIDM_0000018';... -'NIDM_CUSTOM_COORDINATE_SYSTEM','nidm:NIDM_0000017';... -'nidm:CoordinateSpace','nidm:NIDM_0000016';... -'nidm:Coordinate','nidm:NIDM_0000015';... -'NIDM_LEGENDRE_POLYNOMIAL_ORDER','nidm:NIDM_0000014';... -'nidm:ContrastStandardErrorMap','nidm:NIDM_0000013';... -'NIDM_CONNECTIVITY_CRITERION','nidm:NIDM_0000012';... -'nidm:ConjunctionInference','nidm:NIDM_0000011';... -'nidm:hasfMRIDesign','nidm:NIDM_0000010';... -'NIDM_COLIN27_COORDINATE_SYSTEM','nidm:NIDM_0000009';... -'nidm:ClusterLabelsMap','nidm:NIDM_0000008';... -'nidm:ClusterDefinitionCriteria','nidm:NIDM_0000007';... -'NIDM_CLUSTER','nidm:NIDM_0000006';... -'NIDM_BINOMIAL_DISTRIBUTION','nidm:NIDM_0000005';... -'NIDM_BINARY_MAP','nidm:NIDM_0000004';... -'NIDM_ARBITRARILY_CORRELATED_ERROR','nidm:NIDM_0000003';... -'nidm:ContrastEstimation','nidm:NIDM_0000001';... -'nidm:ContrastMap','nidm:NIDM_0000002';... -'obo:Statistic','obo:STATO_0000039';... -'obo:pValueFWER','obo:OBI_0001265';... -'OBO_Q_VALUE_FDR','obo:OBI_0001442';... -'stato:OLS','obo:STATO_0000370';... -'stato:GLS','obo:STATO_0000372';... -'stato:TStatistic','obo:STATO_0000176';... -'stato:FStatistic','obo:STATO_0000282';... -'stato:ZStatistic','obo:STATO_0000376';... -'stato:ContrastWeightMatrix','obo:STATO_0000323';... -'stato:ToeplitzCovarianceStructure','obo:STATO_0000357'}; +'afni:GammaHRF', 'afni_AFNIsGammaHRF';... +'afni:LegendrePolynomialDriftModel', 'afni_AFNIsLegendrePolinomialDriftModel';... +'fsl:FSL_0000001', 'fsl_FSLsGammaDifferenceHRF';... +'fsl:FSL_0000002', 'fsl_GaussianRunningLineDriftModel';... +'fsl:FSL_0000003', 'fsl_FSLsTemporalDerivative';... +'fsl:FSL_0000004', 'fsl_driftCutoffPeriod';... +'fsl:FSL_0000005', 'fsl_featVersion';... +'nidm:NIDM_0000001', 'nidm_ContrastEstimation';... +'nidm:NIDM_0000002', 'nidm_ContrastMap';... +'nidm:NIDM_0000003', 'nidm_ArbitrarilyCorrelatedError';... +'nidm:NIDM_0000004', 'nidm_BinaryMap';... +'nidm:NIDM_0000005', 'nidm_BinomialDistribution';... +'nidm:NIDM_0000006', 'nidm_Cluster';... +'nidm:NIDM_0000007', 'nidm_ClusterDefinitionCriteria';... +'nidm:NIDM_0000008', 'nidm_ClusterLabelsMap';... +'nidm:NIDM_0000009', 'nidm_Colin27CoordinateSystem';... +'nidm:NIDM_0000010', 'nidm_hasfMRIDesign';... +'nidm:NIDM_0000011', 'nidm_ConjunctionInference';... +'nidm:NIDM_0000012', 'nidm_ConnectivityCriterion';... +'nidm:NIDM_0000013', 'nidm_ContrastStandardErrorMap';... +'nidm:NIDM_0000014', 'nidm_LegendrePolynomialOrder';... +'nidm:NIDM_0000015', 'nidm_Coordinate';... +'nidm:NIDM_0000016', 'nidm_CoordinateSpace';... +'nidm:NIDM_0000017', 'nidm_CustomCoordinateSystem';... +'nidm:NIDM_0000018', 'nidm_DataScaling';... +'nidm:NIDM_0000019', 'nidm_DesignMatrix';... +'nidm:NIDM_0000020', 'nidm_DisplayMaskMap';... +'nidm:NIDM_0000021', 'nidm_regressorNames';... +'nidm:NIDM_0000022', 'nidm_ErrorDistribution';... +'nidm:NIDM_0000023', 'nidm_ErrorModel';... +'nidm:NIDM_0000024', 'nidm_ExchangeableError';... +'nidm:NIDM_0000025', 'nidm_ExcursionSetMap';... +'nidm:NIDM_0000026', 'nidm_ExtentThreshold';... +'nidm:NIDM_0000027', 'nidm_NIDMResults';... +'nidm:NIDM_0000028', 'nidm_FiniteImpulseResponseBasisSet';... +'nidm:NIDM_0000029', 'nidm_GammaDifferenceHRF';... +'nidm:NIDM_0000030', 'nidm_GammaBasisSet';... +'nidm:NIDM_0000031', 'nidm_GammaHRF';... +'nidm:NIDM_0000032', 'nidm_GaussianDistribution';... +'nidm:NIDM_0000033', 'nidm_GrandMeanMap';... +'nidm:NIDM_0000034', 'nidm_HeightThreshold';... +'nidm:NIDM_0000035', 'nidm_HemodynamicResponseFunction';... +'nidm:NIDM_0000036', 'nidm_ConvolutionBasisSet';... +'nidm:NIDM_0000037', 'nidm_HemodynamicResponseFunctionDerivative';... +'nidm:NIDM_0000038', 'nidm_Icbm452AirCoordinateSystem';... +'nidm:NIDM_0000039', 'nidm_Icbm452Warp5CoordinateSystem';... +'nidm:NIDM_0000040', 'nidm_IcbmMni152LinearCoordinateSystem';... +'nidm:NIDM_0000041', 'nidm_IcbmMni152NonLinear2009aAsymmetricCoordinateSystem';... +'nidm:NIDM_0000042', 'nidm_IcbmMni152NonLinear2009aSymmetricCoordinateSystem';... +'nidm:NIDM_0000043', 'nidm_IcbmMni152NonLinear2009bAsymmetricCoordinateSystem';... +'nidm:NIDM_0000044', 'nidm_IcbmMni152NonLinear2009bSymmetricCoordinateSystem';... +'nidm:NIDM_0000045', 'nidm_IcbmMni152NonLinear2009cAsymmetricCoordinateSystem';... +'nidm:NIDM_0000046', 'nidm_IcbmMni152NonLinear2009cSymmetricCoordinateSystem';... +'nidm:NIDM_0000047', 'nidm_IcbmMni152NonLinear6thGenerationCoordinateSystem';... +'nidm:NIDM_0000048', 'nidm_IndependentError';... +'nidm:NIDM_0000049', 'nidm_Inference';... +'nidm:NIDM_0000050', 'nidm_Ixi549CoordinateSystem';... +'nidm:NIDM_0000051', 'nidm_MNICoordinateSystem';... +'nidm:NIDM_0000052', 'nidm_Map';... +'nidm:NIDM_0000053', 'nidm_MapHeader';... +'nidm:NIDM_0000054', 'nidm_MaskMap';... +'nidm:NIDM_0000055', 'nidm_Mni305CoordinateSystem';... +'nidm:NIDM_0000056', 'nidm_ModelParametersEstimation';... +'nidm:NIDM_0000057', 'nidm_NIDMObjectModel';... +'nidm:NIDM_0000058', 'nidm_NonParametricDistribution';... +'nidm:NIDM_0000059', 'nidm_NonParametricSymmetricDistribution';... +'nidm:NIDM_0000060', 'nidm_OneTailedTest';... +'nidm:NIDM_0000061', 'nidm_ParameterEstimateMap';... +'nidm:NIDM_0000062', 'nidm_Peak';... +'nidm:NIDM_0000063', 'nidm_PeakDefinitionCriteria';... +'nidm:NIDM_0000064', 'nidm_PixelConnectivityCriterion';... +'nidm:NIDM_0000065', 'nidm_PoissonDistribution';... +'nidm:NIDM_0000066', 'nidm_ResidualMeanSquaresMap';... +'nidm:NIDM_0000067', 'nidm_CustomBasisSet';... +'nidm:NIDM_0000068', 'nidm_SearchSpaceMaskMap';... +'nidm:NIDM_0000069', 'nidm_FourierBasisSet';... +'nidm:NIDM_0000070', 'nidm_SupraThresholdCluster';... +'nidm:NIDM_0000071', 'nidm_ErrorParameterMapWiseDependence';... +'nidm:NIDM_0000072', 'nidm_ConstantParameter';... +'nidm:NIDM_0000073', 'nidm_IndependentParameter';... +'nidm:NIDM_0000074', 'nidm_RegularizedParameter';... +'nidm:NIDM_0000075', 'nidm_StandardizedCoordinateSystem';... +'nidm:NIDM_0000076', 'nidm_StatisticMap';... +'nidm:NIDM_0000077', 'nidm_SubjectCoordinateSystem';... +'nidm:NIDM_0000078', 'nidm_TalairachCoordinateSystem';... +'nidm:NIDM_0000079', 'nidm_TwoTailedTest';... +'nidm:NIDM_0000080', 'nidm_VoxelConnectivityCriterion';... +'nidm:NIDM_0000081', 'nidm_WorldCoordinateSystem';... +'nidm:NIDM_0000082', 'nidm_clusterLabelId';... +'nidm:NIDM_0000083', 'nidm_clusterSizeInVertices';... +'nidm:NIDM_0000084', 'nidm_clusterSizeInVoxels';... +'nidm:NIDM_0000085', 'nidm_contrastName';... +'nidm:NIDM_0000086', 'nidm_coordinateVector';... +'nidm:NIDM_0000087', 'nidm_DriftModel';... +'nidm:NIDM_0000088', 'nidm_hasDriftModel';... +'nidm:NIDM_0000089', 'nidm_dependenceMapWiseDependence';... +'nidm:NIDM_0000090', 'nidm_dimensionsInVoxels';... +'nidm:NIDM_0000091', 'nidm_effectDegreesOfFreedom';... +'nidm:NIDM_0000092', 'nidm_equivalentZStatistic';... +'nidm:NIDM_0000093', 'nidm_errorDegreesOfFreedom';... +'nidm:NIDM_0000094', 'nidm_errorVarianceHomogeneous';... +'nidm:NIDM_0000095', 'nidm_partialConjunctionDegree';... +'nidm:NIDM_0000096', 'nidm_grandMeanScaling';... +'nidm:NIDM_0000097', 'nidm_hasAlternativeHypothesis';... +'nidm:NIDM_0000098', 'nidm_hasClusterLabelsMap';... +'nidm:NIDM_0000099', 'nidm_hasConnectivityCriterion';... +'nidm:NIDM_0000100', 'nidm_hasErrorDependence';... +'nidm:NIDM_0000101', 'nidm_hasErrorDistribution';... +'nidm:NIDM_0000102', 'nidm_hasHRFBasis';... +'nidm:NIDM_0000103', 'nidm_hasMapHeader';... +'nidm:NIDM_0000104', 'nidm_inCoordinateSpace';... +'nidm:NIDM_0000105', 'nidm_inWorldCoordinateSystem';... +'nidm:NIDM_0000106', 'nidm_isUserDefined';... +'nidm:NIDM_0000107', 'nidm_maskedMedian';... +'nidm:NIDM_0000108', 'nidm_maxNumberOfPeaksPerCluster';... +'nidm:NIDM_0000109', 'nidm_minDistanceBetweenPeaks';... +'nidm:NIDM_0000110', 'nidm_GaussianHRF';... +'nidm:NIDM_0000111', 'nidm_numberOfSupraThresholdClusters';... +'nidm:NIDM_0000112', 'nidm_numberOfDimensions';... +'nidm:NIDM_0000113', 'nidm_objectModel';... +'nidm:NIDM_0000114', 'nidm_pValue';... +'nidm:NIDM_0000115', 'nidm_pValueFWER';... +'nidm:NIDM_0000116', 'nidm_pValueUncorrected';... +'nidm:NIDM_0000117', 'nidm_pixel4connected';... +'nidm:NIDM_0000118', 'nidm_pixel8connected';... +'nidm:NIDM_0000119', 'nidm_qValueFDR';... +'nidm:NIDM_0000120', 'nidm_randomFieldStationarity';... +'nidm:NIDM_0000121', 'nidm_searchVolumeInVoxels';... +'nidm:NIDM_0000122', 'nidm_softwareVersion';... +'nidm:NIDM_0000123', 'nidm_statisticType';... +'nidm:NIDM_0000124', 'nidm_targetIntensity';... +'nidm:NIDM_0000125', 'nidm_userSpecifiedThresholdType';... +'nidm:NIDM_0000126', 'nidm_varianceMapWiseDependence';... +'nidm:NIDM_0000127', 'nidm_version';... +'nidm:NIDM_0000128', 'nidm_voxel18connected';... +'nidm:NIDM_0000129', 'nidm_voxel26connected';... +'nidm:NIDM_0000130', 'nidm_voxel6connected';... +'nidm:NIDM_0000131', 'nidm_voxelSize';... +'nidm:NIDM_0000132', 'nidm_voxelToWorldMapping';... +'nidm:NIDM_0000133', 'nidm_voxelUnits';... +'nidm:NIDM_0000134', 'nidm_withEstimationMethod';... +'nidm:NIDM_0000135', 'nidm_ContrastVarianceMap';... +'nidm:NIDM_0000136', 'nidm_searchVolumeInUnits';... +'nidm:NIDM_0000137', 'nidm_searchVolumeInVertices';... +'nidm:NIDM_0000138', 'nidm_hasMaximumIntensityProjection';... +'nidm:NIDM_0000139', 'nidm_coordinateVectorInVoxels';... +'nidm:NIDM_0000140', 'nidm_ClusterCenterOfGravity';... +'nidm:NIDM_0000141', 'nidm_expectedNumberOfClusters';... +'nidm:NIDM_0000142', 'nidm_expectedNumberOfVerticesPerCluster';... +'nidm:NIDM_0000143', 'nidm_expectedNumberOfVoxelsPerCluster';... +'nidm:NIDM_0000144', 'nidm_ReselsPerVoxelMap';... +'nidm:NIDM_0000145', 'nidm_noiseRoughnessInVoxels';... +'nidm:NIDM_0000146', 'nidm_heightCriticalThresholdFDR05';... +'nidm:NIDM_0000147', 'nidm_heightCriticalThresholdFWE05';... +'nidm:NIDM_0000148', 'nidm_reselSizeInVoxels';... +'nidm:NIDM_0000149', 'nidm_searchVolumeInResels';... +'nidm:NIDM_0000150', 'nidm_LinearSplineBasisSet';... +'nidm:NIDM_0000151', 'nidm_SineBasisSet';... +'nidm:NIDM_0000152', 'nidm_BlockBasedDesign';... +'nidm:NIDM_0000153', 'nidm_EventRelatedDesign';... +'nidm:NIDM_0000154', 'nidm_MixedDesign';... +'nidm:NIDM_0000155', 'nidm_fMRIDesignType';... +'nidm:NIDM_0000156', 'nidm_clusterSizeInResels';... +'nidm:NIDM_0000157', 'nidm_noiseFWHMInUnits';... +'nidm:NIDM_0000158', 'nidm_noiseFWHMInVertices';... +'nidm:NIDM_0000159', 'nidm_noiseFWHMInVoxels';... +'nidm:NIDM_0000160', 'nidm_PValueUncorrected';... +'nidm:NIDM_0000161', 'nidm_equivalentThreshold';... +'nidm:NIDM_0000162', 'nidm_Threshold';... +'nidm:NIDM_0000163', 'nidm_ContrastExplainedMeanSquareMap';... +'nidm:NIDM_0000164', 'nidm_NeuroimagingAnalysisSoftware';... +'nidm:NIDM_0000165', 'nidm_NIDMResultsExporter';... +'nidm:NIDM_0000166', 'nidm_NIDMResultsExport';... +'nidm:NIDM_0000167', 'nidm_nidmfsl';... +'nidm:NIDM_0000168', 'nidm_spm_results_nidm';... +'spm:SPM_0000001', 'spm_SPMsDriftCutoffPeriod';... +'spm:SPM_0000002', 'spm_DCTDriftModel';... +'spm:SPM_0000003', 'spm_SPMsDispersionDerivative';... +'spm:SPM_0000004', 'spm_SPMsCanonicalHRF';... +'spm:SPM_0000005', 'spm_PartialConjunctionInference';... +'spm:SPM_0000006', 'spm_SPMsTemporalDerivative';... +'spm:SPM_0000007', 'spm_noiseFWHMInUnits';... +'spm:SPM_0000008', 'spm_noiseFWHMInVertices';... +'spm:SPM_0000009', 'spm_noiseFWHMInVoxels';... +'spm:SPM_0000010', 'spm_searchVolumeReselsGeometry';... +'spm:SPM_0000011', 'spm_smallestSupraThresholdClusterSizeInVerticesFDR05';... +'spm:SPM_0000012', 'spm_smallestSupraThresholdClusterSizeInVerticesFWE05';... +'spm:SPM_0000013', 'spm_smallestSupraThresholdClusterSizeInVoxelsFDR05';... +'spm:SPM_0000014', 'spm_smallestSupraThresholdClusterSizeInVoxelsFWE05';... +'spm:SPM_0000015', 'spm_partialConjunctionDegree';... +}; + +C = [C; {... +'neurolex:nif-0000-00343', 'neurolex_SPM';... +'obo:STATO_0000039', 'obo_Statistic';... +'obo:OBI_0001265', 'obo_pValueFWER';... +'obo:OBI_0001442', 'obo_qValueFDR';... +'obo:STATO_0000370', 'stato_OLS';... +'obo:STATO_0000372', 'stato_GLS';... +'obo:STATO_0000176', 'stato_TStatistic';... +'obo:STATO_0000227', 'stato_GaussianDistribution';... +'obo:STATO_0000282', 'stato_FStatistic';... +'obo:STATO_0000376', 'stato_ZStatistic';... +'obo:STATO_0000323', 'stato_ContrastWeightMatrix';... +'obo:STATO_0000357', 'stato_ToeplitzCovarianceStructure';... +'obo:STATO_0000405', 'stato_UnstructuredCorrelationStructure';... +}]; diff --git a/spm_results_ui.m b/spm_results_ui.m index d4599b74..9a739368 100644 --- a/spm_results_ui.m +++ b/spm_results_ui.m @@ -124,7 +124,7 @@ % Copyright (C) 1996-2013 Wellcome Trust Centre for Neuroimaging % Karl Friston & Andrew Holmes -% $Id: spm_results_ui.m 6416 2015-04-21 15:34:10Z guillaume $ +% $Id: spm_results_ui.m 6647 2015-12-14 19:12:54Z guillaume $ %========================================================================== @@ -236,7 +236,7 @@ % warning statements from MATLAB. %__________________________________________________________________________ -SVNid = '$Rev: 6416 $'; +SVNid = '$Rev: 6647 $'; %-Condition arguments %-------------------------------------------------------------------------- diff --git a/spm_robust_average.m b/spm_robust_average.m index 775eb774..83bfc35a 100644 --- a/spm_robust_average.m +++ b/spm_robust_average.m @@ -10,7 +10,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % James Kilner -% $Id: spm_robust_average.m 5856 2014-01-29 14:28:33Z vladimir $ +% $Id: spm_robust_average.m 6650 2015-12-17 14:48:43Z vladimir $ if nargin < 3 || isempty(ks) ks = 3; @@ -43,11 +43,14 @@ X = reshape(X, size(X, 1), []); end +%-Replace Inf with NaN +%-------------------------------------------------------------------------- +X(~isfinite(X)) = NaN; + %-Rescale the data %-------------------------------------------------------------------------- [X, scalefactor] = spm_cond_units(X); - %-Actual robust averaging %-------------------------------------------------------------------------- ores=1; diff --git a/spm_sample_vol.mexa64 b/spm_sample_vol.mexa64 index 73f5b57d..d0b018b0 100755 Binary files a/spm_sample_vol.mexa64 and b/spm_sample_vol.mexa64 differ diff --git a/spm_sample_vol.mexmaci64 b/spm_sample_vol.mexmaci64 index 0b327680..b20bea25 100755 Binary files a/spm_sample_vol.mexmaci64 and b/spm_sample_vol.mexmaci64 differ diff --git a/spm_sample_vol.mexw32 b/spm_sample_vol.mexw32 index f01b096a..55b2afe4 100755 Binary files a/spm_sample_vol.mexw32 and b/spm_sample_vol.mexw32 differ diff --git a/spm_sample_vol.mexw64 b/spm_sample_vol.mexw64 index c3e56139..2e9750e0 100755 Binary files a/spm_sample_vol.mexw64 and b/spm_sample_vol.mexw64 differ diff --git a/spm_select.m b/spm_select.m index 4f317296..e58e7e9f 100644 --- a/spm_select.m +++ b/spm_select.m @@ -17,7 +17,7 @@ % 'xml' - XML files % 'mat' - MATLAB .mat files or .txt files (assumed to contain % ASCII representation of a 2D-numeric array) -% 'batch' - SPM batch files (.m, .mat and XML) +% 'batch' - SPM batch files (.m or .mat) % 'dir' - select a directory % Other strings act as a filter to regexp. This means that % e.g. DCM*.mat files should have a typ of '^DCM.*\.mat$' @@ -61,7 +61,7 @@ % Copyright (C) 2005-2015 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_select.m 6444 2015-05-21 11:15:48Z guillaume $ +% $Id: spm_select.m 6530 2015-08-21 14:43:52Z guillaume $ % For developers: diff --git a/spm_slice_vol.mexa64 b/spm_slice_vol.mexa64 index 1e7cf39f..5a3ece5c 100755 Binary files a/spm_slice_vol.mexa64 and b/spm_slice_vol.mexa64 differ diff --git a/spm_slice_vol.mexmaci64 b/spm_slice_vol.mexmaci64 index c2175032..5ae28af7 100755 Binary files a/spm_slice_vol.mexmaci64 and b/spm_slice_vol.mexmaci64 differ diff --git a/spm_slice_vol.mexw32 b/spm_slice_vol.mexw32 index c2470d36..1410517a 100755 Binary files a/spm_slice_vol.mexw32 and b/spm_slice_vol.mexw32 differ diff --git a/spm_slice_vol.mexw64 b/spm_slice_vol.mexw64 index 9ab31814..14db01b1 100755 Binary files a/spm_slice_vol.mexw64 and b/spm_slice_vol.mexw64 differ diff --git a/spm_softmax.m b/spm_softmax.m index aebded57..fc852ce2 100644 --- a/spm_softmax.m +++ b/spm_softmax.m @@ -14,19 +14,24 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_softmax.m 6290 2014-12-20 22:11:50Z karl $ +% $Id: spm_softmax.m 6655 2015-12-23 20:21:27Z karl $ % apply %-------------------------------------------------------------------------- -if nargin == 1, k = 1; end +if nargin > 1, x = k*x; end -n = size(x,2); -if n > 1 - for i = 1:n - y(:,i) = spm_softmax(x(:,i),k); +n = size(x); +if n(end) > 1 + ind = cell(size(n)); + ind(:) = {':'}; + for i = 1:n(end) + sub = ind; + sub(end) = {i}; + y(sub{:}) = spm_softmax(x(sub{:})); end else - x = k*(x - max(x)); - y = exp(x)/sum(exp(x)); + x = x - max(x); + ex = exp(x); + y = ex/sum(ex); end diff --git a/spm_spm.m b/spm_spm.m index 2e890bac..6eef7009 100644 --- a/spm_spm.m +++ b/spm_spm.m @@ -265,13 +265,13 @@ % Analysis of fMRI Time-Series Revisited - Again. Worsley KJ, Friston KJ. % (1995) NeuroImage 2:173-181. %__________________________________________________________________________ -% Copyright (C) 1994-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1994-2016 Wellcome Trust Centre for Neuroimaging % Karl Friston & Guillaume Flandin -% $Id: spm_spm.m 6015 2014-05-23 15:46:19Z guillaume $ +% $Id: spm_spm.m 6678 2016-01-14 18:23:33Z guillaume $ -SVNid = '$Rev: 6015 $'; +SVNid = '$Rev: 6678 $'; %-Say hello %-------------------------------------------------------------------------- @@ -327,8 +327,11 @@ YNaNrep = spm_type(VY(1).dt(1),'nanrep'); if spm_mesh_detect(VY) file_ext = '.gii'; + g = VY(1).private; + metadata = {g.private.metadata(1).name, g.private.metadata(1).value}; else file_ext = spm_file_ext; + metadata = {}; end %-Delete files from previous analyses @@ -459,7 +462,8 @@ 'dt', [spm_type('uint8') spm_platform('bigend')],... 'mat', M,... 'pinfo', [1 0 0]',... - 'descrip', 'spm_spm:resultant analysis mask'); + 'descrip', 'spm_spm:resultant analysis mask',... + metadata{:}); VM = spm_data_hdr_write(VM); %-Initialise beta files @@ -470,7 +474,8 @@ 'dt', [spm_type('float32') spm_platform('bigend')],... 'mat', M,... 'pinfo', [1 0 0]',... - 'descrip', 'spm_spm:beta')); + 'descrip', 'spm_spm:beta',... + metadata{:})); for i = 1:nBeta Vbeta(i).fname = [sprintf('beta_%04d',i) file_ext]; @@ -486,7 +491,8 @@ 'dt', [spm_type('float64') spm_platform('bigend')],... 'mat', M,... 'pinfo', [1 0 0]',... - 'descrip', 'spm_spm:Residual sum-of-squares'); + 'descrip', 'spm_spm:Residual sum-of-squares',... + metadata{:}); VResMS = spm_data_hdr_write(VResMS); %-Initialise standardised residual images @@ -499,7 +505,8 @@ 'dt', [spm_type('float64') spm_platform('bigend')],... 'mat', M,... 'pinfo', [1 0 0]',... - 'descrip', 'spm_spm:StandardisedResiduals')); + 'descrip', 'spm_spm:StandardisedResiduals',... + metadata{:})); if resInMem, for i=1:nSres, VResI(i).dat = zeros(VResI(i).dim); end; end for i = 1:nSres @@ -652,18 +659,18 @@ 'dt', [spm_type('float64') spm_platform('bigend')],... 'mat', M,... 'pinfo', [1 0 0]',... - 'descrip', 'spm_spm: resels per voxel'); + 'descrip', 'spm_spm: resels per voxel',... + metadata{:}); VRpv = spm_data_hdr_write(VRpv); ResI = zeros(prod(DIM),numel(VResI)); for i=1:numel(VResI) ResI(:,i) = spm_data_read(VResI(i)); end - g = gifti(VY(1).fname); - g = g.private.metadata(1).value; + g = metadata{2}; if isempty(spm_file(g,'path')) g = fullfile(spm_file(VY(1).fname,'path'),g); end - [R, RPV] = spm_mesh_resels(gifti(g),mask,ResI); + [R, RPV] = spm_mesh_resels(gifti(g),mask,ResI,[nScan erdf]); RPV(~mask) = NaN; VRpv = spm_data_write(VRpv,RPV); FWHM = [1 1 1] * (1/mean(RPV(mask))).^(1/3); diff --git a/spm_summarise.m b/spm_summarise.m index 208b72d4..5f92c19b 100644 --- a/spm_summarise.m +++ b/spm_summarise.m @@ -25,7 +25,7 @@ % Copyright (C) 2010-2015 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin, Ged Ridgway -% $Id: spm_summarise.m 6448 2015-05-22 18:31:04Z guillaume $ +% $Id: spm_summarise.m 6490 2015-06-26 11:51:46Z guillaume $ %-Argument checks %-------------------------------------------------------------------------- @@ -34,7 +34,7 @@ if ~sts, error('Must select 1 or more images'), end end if iscellstr(V), V = char(V); end -if ischar(V), V = spm_vol(V); end +if ischar(V), V = spm_data_hdr_read(V); end spm_check_orientations(V); if nargin < 2 || isempty(xY), xY = struct; end @@ -80,7 +80,7 @@ XYZ = round(V(1).mat \ [xY.XYZmm; ones(1, size(xY.XYZmm, 2))]); % Run on first volume to determine p, and transpose if column vector -Y = fhandle(dropNaNs(spm_get_data(V(1), XYZ))); +Y = fhandle(dropNaNs(spm_data_read(V(1), 'xyz', XYZ))); if ndims(Y) > 2 error('Function must return a [1 x p] array') elseif size(Y, 1) ~= 1 @@ -95,5 +95,5 @@ % Preallocate space and then run on remaining volumes Y(2:numel(V), :) = 0; for i = 2:numel(V) - Y(i, :) = fhandle(dropNaNs(spm_get_data(V(i), XYZ))); + Y(i, :) = fhandle(dropNaNs(spm_data_read(V(i), 'xyz', XYZ))); end diff --git a/spm_surf.m b/spm_surf.m index ec6c9c8e..2b3013ae 100644 --- a/spm_surf.m +++ b/spm_surf.m @@ -50,12 +50,12 @@ % 0.5. The input segmentation images can be manually cleaned up first using % e.g., MRIcron. %__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2002-2015 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_surf.m 5101 2012-12-07 18:23:20Z guillaume $ +% $Id: spm_surf.m 6520 2015-08-13 16:13:06Z guillaume $ -SVNrev = '$Rev: 5101 $'; +SVNrev = '$Rev: 6520 $'; spm('FnBanner',mfilename,SVNrev); spm('FigName','Surface'); @@ -64,10 +64,14 @@ %-------------------------------------------------------------------------- try if isstruct(P) - job = P; - P = char(job.data); - mode = job.mode; - thresh = job.thresh; + if all(isfield(P,{'data','mode','thresh'})) + job = P; + P = char(job.data); + mode = job.mode; + thresh = job.thresh; + else + % P is an spm_vol struct array + end end catch [P, sts] = spm_select([1 Inf],'image','Select images'); @@ -113,10 +117,10 @@ V = spm_vol(P); br = zeros(V(1).dim(1:3)); -for i=1:V(1).dim(3), +for i=1:V(1).dim(3) B = spm_matrix([0 0 i]); tmp = spm_slice_vol(V(1),B,V(1).dim(1:2),1); - for j=2:length(V), + for j=2:numel(V) M = V(j).mat\V(1).mat*B; tmp = tmp + spm_slice_vol(V(j),M,V(1).dim(1:2),1); end @@ -125,11 +129,11 @@ % Build a 3x3x3 seperable smoothing kernel and smooth %-------------------------------------------------------------------------- -kx=[0.75 1 0.75]; -ky=[0.75 1 0.75]; -kz=[0.75 1 0.75]; -sm=sum(kron(kron(kz,ky),kx))^(1/3); -kx=kx/sm; ky=ky/sm; kz=kz/sm; +kx = [0.75 1 0.75]; +ky = [0.75 1 0.75]; +kz = [0.75 1 0.75]; +sm = sum(kron(kron(kz,ky),kx))^(1/3); +kx = kx/sm; ky = ky/sm; kz = kz/sm; spm_conv_vol(br,br,kx,ky,kz,-[1 1 1]); [pth,nam] = spm_fileparts(V(1).fname); diff --git a/spm_t2z.m b/spm_t2z.m index f6f32329..73128287 100644 --- a/spm_t2z.m +++ b/spm_t2z.m @@ -1,6 +1,6 @@ function [z,t1,z1] = spm_t2z(t,df,Tol) -% Students t to standard Normal (z-score) distribution -% FORMAT [z,t1] = spm_t2z(t,df,Tol); +% Student's t to standard Normal (z-score) distribution +% FORMAT [z,t1,z1] = spm_t2z(t,df,Tol) % t - t values % df - degrees of freedom % Tol - minimum tail probability for direct computation @@ -51,10 +51,10 @@ % (though still significant) results. % %__________________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1994-2015 Wellcome Trust Centre for Neuroimaging % Andrew Holmes -% $Id: spm_t2z.m 1143 2008-02-07 19:33:33Z spm $ +% $Id: spm_t2z.m 6654 2015-12-22 12:55:36Z spm $ %-Initialisation @@ -79,10 +79,10 @@ % betainc(1,*,*) and betainc(0,*,*) warn "Log of zero" %--------------------------------------------------------------------------- Qi = find(isinf(t)); -if length(Qi), z(Qi)=t(Qi); end +if ~isempty(Qi), z(Qi)=t(Qi); end tmp = df./(df + t.^2); Q = find(tmp~=1 & ~isinf(t)); -if ~length(Q); return; end +if isempty(Q), return; end %-Mask out at +/- t1 for interpolation %--------------------------------------------------------------------------- diff --git a/spm_tests.m b/spm_tests.m index 5465657d..2b8a91e3 100644 --- a/spm_tests.m +++ b/spm_tests.m @@ -7,6 +7,7 @@ % coverage: display code coverage [default: false] % tag: test tag selector [default: '', ie all tests] % tap: save a Test Anything Protocol (TAP) file [default: false] +% test: name of function to test [default: '', ie all tests] % % results - TestResult array containing information describing the % result of running the test suite. @@ -14,20 +15,20 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_tests.m 6416 2015-04-21 15:34:10Z guillaume $ +% $Id: spm_tests.m 6492 2015-06-26 14:27:40Z guillaume $ if spm_check_version('matlab','8.3') < 0 error('Unit Tests require MATLAB R2014a or above.'); end -SVNid = '$Rev: 6416 $'; +SVNid = '$Rev: 6492 $'; SPMid = spm('FnBanner',mfilename,SVNid); %-Input parameters %-------------------------------------------------------------------------- options = struct('verbose',2, 'display',false, 'coverage',false, ... - 'tag', '', 'tap',false); + 'tag', '', 'tap',false, 'test',''); if nargin if isstruct(varargin{1}) fn = fieldnames(varargin{1}); @@ -49,7 +50,16 @@ import matlab.unittest.TestSuite; import matlab.unittest.selectors.*; tests = fullfile(spm('Dir'),'tests'); -suite = TestSuite.fromFolder(tests, 'IncludingSubfolders', true); +if isempty(options.test) + suite = TestSuite.fromFolder(tests, 'IncludingSubfolders', true); +else + mtest = fullfile(tests,['test_' spm_file(options.test,'ext','.m')]); + if ~spm_existfile(mtest) + warning('SPM:tests:fileNotFound','No tests found for %s',options.test); + return; + end + suite = TestSuite.fromFile(mtest); +end if ~isempty(options.tag) suite = suite.selectIf(~HasTag | HasTag(options.tag)); end diff --git a/spm_type.m b/spm_type.m index 24eb48b7..aa5c21de 100644 --- a/spm_type.m +++ b/spm_type.m @@ -9,6 +9,7 @@ % - 'nanrep' - return 1 if there is a NaN representation. % - 'bits' - return the number of bits per voxel. % - 'intt' - return 1 if values rounded to nearest integer. +% - 'conv' - return conversion function handle. %__________________________________________________________________________ % % Format specifiers are based on NIFTI-1. @@ -20,10 +21,10 @@ % % With no arguments, a list of data types is returned. %__________________________________________________________________________ -% Copyright (C) 1996-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 1996-2015 Wellcome Trust Centre for Neuroimaging % John Ashburner & Andrew Holmes -% $Id: spm_type.m 5925 2014-03-20 16:47:44Z guillaume $ +% $Id: spm_type.m 6656 2015-12-24 16:49:52Z guillaume $ prec = {'uint8','int16','int32','float32','float64','int8','uint16','uint32'}; conv = {@uint8,@int16,@int32,@single,@double,@int8,@uint16,@uint32}; @@ -42,7 +43,11 @@ if ischar(x) sel = find(strcmpi(prec,deblank(x))); else - sel = find(ismember(types,x)); + if numel(x) == 1 + sel = find(types == x); + else + sel = find(ismember(types,x)); + end end if nargin == 1 if ischar(x) diff --git a/spm_uc_FDR.m b/spm_uc_FDR.m index 9cfaddda..2c5cffde 100644 --- a/spm_uc_FDR.m +++ b/spm_uc_FDR.m @@ -76,10 +76,10 @@ % in multiple testing under dependency". To appear, Annals of Statistics. % Available at http://www.math.tau.ac.il/~benja %__________________________________________________________________________ -% Copyright (C) 2002-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2002-2015 Wellcome Trust Centre for Neuroimaging % Thomas Nichols -% $Id: spm_uc_FDR.m 5824 2014-01-02 14:50:13Z guillaume $ +% $Id: spm_uc_FDR.m 6643 2015-12-11 16:57:25Z guillaume $ if (nargin<6), Vm = []; end @@ -92,14 +92,14 @@ % Load, mask & sort statistic image (if needed) %-------------------------------------------------------------------------- if isstruct(Vs) - Ts = spm_read_vols(Vs(1)); - for i = 2:length(Vs) - Ts = min(Ts,spm_read_vols(Vs(i))); + Ts = spm_data_read(Vs(1)); + for i=2:numel(Vs) + Ts = min(Ts,spm_data_read(Vs(i))); end if ~isempty(Vm) if isstruct(Vm) - Ts(spm_read_vols(Vm)==0) = []; - elseif (numel(Vm)==1) + Ts(spm_data_read(Vm)==0) = []; + elseif numel(Vm)==1 Ts(Ts==Vm) = []; else Ts = Ts(Vm); diff --git a/spm_unlink.mexw32 b/spm_unlink.mexw32 index 1c556156..e1a144e2 100755 Binary files a/spm_unlink.mexw32 and b/spm_unlink.mexw32 differ diff --git a/spm_unlink.mexw64 b/spm_unlink.mexw64 index 7652a9f8..d1679beb 100755 Binary files a/spm_unlink.mexw64 and b/spm_unlink.mexw64 differ diff --git a/spm_update.m b/spm_update.m index eff5d853..274ab74c 100644 --- a/spm_update.m +++ b/spm_update.m @@ -18,10 +18,10 @@ % n - new revision is available for download % msg - string describing outcome, that would otherwise be displayed. %__________________________________________________________________________ -% Copyright (C) 2010-2015 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2010-2016 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_update.m 6469 2015-06-03 16:46:08Z guillaume $ +% $Id: spm_update.m 6684 2016-01-15 16:30:24Z guillaume $ vspm = spm('Ver'); @@ -63,7 +63,7 @@ error('SPM cannot be found in MATLAB path.'); end if ~strcmp(v,vspm), error('Your SPM version is %s and not %s',v,vspm); end -rs = [6225 6470]; +rs = [6225 6470 6685]; if isnan(r), r = rs(1); end if floor(r) == str2double(vspm(4:end)) try diff --git a/spm_voronoi.mexw32 b/spm_voronoi.mexw32 index 42f5152b..49b58a04 100755 Binary files a/spm_voronoi.mexw32 and b/spm_voronoi.mexw32 differ diff --git a/spm_voronoi.mexw64 b/spm_voronoi.mexw64 index 1f35b996..65c73ced 100755 Binary files a/spm_voronoi.mexw64 and b/spm_voronoi.mexw64 differ diff --git a/spm_write_residuals.m b/spm_write_residuals.m index 8f33f78d..74547221 100644 --- a/spm_write_residuals.m +++ b/spm_write_residuals.m @@ -10,7 +10,7 @@ % Copyright (C) 2012-2013 Wellcome Trust Centre for Neuroimaging % Guillaume Flandin -% $Id: spm_write_residuals.m 5575 2013-07-08 16:38:51Z guillaume $ +% $Id: spm_write_residuals.m 6656 2015-12-24 16:49:52Z guillaume $ %-Get SPM.mat @@ -55,7 +55,7 @@ %========================================================================== spm('Pointer','Watch') M = SPM.xY.VY(1).mat; -DIM = SPM.xY.VY(1).dim(1:3); +DIM = SPM.xY.VY(1).dim(1:min(numel(SPM.xY.VY(1).dim),3)); [nScan, nBeta] = size(SPM.xX.X); if spm_mesh_detect(SPM.xY.VY) diff --git a/src/Makefile b/src/Makefile index ae491e47..b5845bc9 100644 --- a/src/Makefile +++ b/src/Makefile @@ -3,7 +3,7 @@ # # Copyright (C) 1991-2015 Wellcome Trust Centre for Neuroimaging # -# $Id: Makefile 6402 2015-04-09 18:11:06Z guillaume $ +# $Id: Makefile 6589 2015-11-03 16:01:08Z guillaume $ # ############################################################################### # @@ -51,17 +51,14 @@ SPMMEX =\ spm_dilate_erode.$(SUF) spm_bwlabel.$(SUF) spm_get_lm.$(SUF)\ spm_voronoi.$(SUF) spm_mesh_utils.$(SUF) \ spm_mrf.$(SUF) spm_diffeo.$(SUF) spm_field.$(SUF) \ - spm_cat.$(SUF) + spm_cat.$(SUF) spm_jsonread.$(SUF) SUBDIRS =\ @file_array/private \ + @gifti/private \ @xmltree/private \ toolbox/FieldMap -ifeq (mex,$(SUF)) - SUBDIRS += @gifti/private -endif - ############################################################################### # Public make targets ############################################################################### @@ -294,7 +291,10 @@ spm_diffeo.$(SUF): spm_diffeo.c shoot_diffeo3d.c shoot_optim3d.c shoot_multiscal $(MEX) spm_diffeo.c shoot_diffeo3d.c shoot_optim3d.c shoot_multiscale.c shoot_regularisers.c shoot_expm3.c shoot_invdef.c shoot_dartel.c shoot_boundary.c shoot_bsplines.c bsplines.c -DIMAGE_SINGLE $(MEXEND) spm_field.$(SUF): spm_field.c shoot_optimN.c shoot_multiscale.c shoot_boundary.c - $(MEX) spm_field.c shoot_optimN.c shoot_multiscale.c shoot_boundary.c $(MEXEND) + $(MEX) spm_field.c shoot_optimN.c shoot_multiscale.c shoot_boundary.c $(MEXEND) + +spm_jsonread.$(SUF): spm_jsonread.c jsmn.h jsmn.c + $(MEX) spm_jsonread.c jsmn.c -DJSMN_PARENT_LINKS $(MEXEND) ############################################################################### # Display Messages diff --git a/src/jsmn.c b/src/jsmn.c new file mode 100644 index 00000000..12cf1f80 --- /dev/null +++ b/src/jsmn.c @@ -0,0 +1,336 @@ +/* +Copyright (c) 2010 Serge A. Zaitsev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#include + +#include "jsmn.h" + +/** + * Allocates a fresh unused token from the token pull. + */ +static jsmntok_t *jsmn_alloc_token(jsmn_parser *parser, + jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *tok; + if (parser->toknext >= num_tokens) { + return NULL; + } + tok = &tokens[parser->toknext++]; + tok->start = tok->end = -1; + tok->size = 0; +#ifdef JSMN_PARENT_LINKS + tok->parent = -1; +#endif + return tok; +} + +/** + * Fills token type and boundaries. + */ +static void jsmn_fill_token(jsmntok_t *token, jsmntype_t type, + int start, int end) { + token->type = type; + token->start = start; + token->end = end; + token->size = 0; +} + +/** + * Fills next available token with JSON primitive. + */ +static int jsmn_parse_primitive(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + int start; + + start = parser->pos; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + switch (js[parser->pos]) { +#ifndef JSMN_STRICT + /* In strict mode primitive must be followed by "," or "}" or "]" */ + case ':': +#endif + case '\t' : case '\r' : case '\n' : case ' ' : + case ',' : case ']' : case '}' : + goto found; + } + if (js[parser->pos] < 32 || js[parser->pos] >= 127) { + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } +#ifdef JSMN_STRICT + /* In strict mode primitive must be followed by a comma/object/array */ + parser->pos = start; + return JSMN_ERROR_PART; +#endif + +found: + if (tokens == NULL) { + parser->pos--; + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_PRIMITIVE, start, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + parser->pos--; + return 0; +} + +/** + * Filsl next token with JSON string. + */ +static int jsmn_parse_string(jsmn_parser *parser, const char *js, + size_t len, jsmntok_t *tokens, size_t num_tokens) { + jsmntok_t *token; + + int start = parser->pos; + + parser->pos++; + + /* Skip starting quote */ + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c = js[parser->pos]; + + /* Quote: end of string */ + if (c == '\"') { + if (tokens == NULL) { + return 0; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) { + parser->pos = start; + return JSMN_ERROR_NOMEM; + } + jsmn_fill_token(token, JSMN_STRING, start+1, parser->pos); +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + return 0; + } + + /* Backslash: Quoted symbol expected */ + if (c == '\\' && parser->pos + 1 < len) { + int i; + parser->pos++; + switch (js[parser->pos]) { + /* Allowed escaped symbols */ + case '\"': case '/' : case '\\' : case 'b' : + case 'f' : case 'r' : case 'n' : case 't' : + break; + /* Allows escaped symbol \uXXXX */ + case 'u': + parser->pos++; + for(i = 0; i < 4 && parser->pos < len && js[parser->pos] != '\0'; i++) { + /* If it isn't a hex character we have an error */ + if(!((js[parser->pos] >= 48 && js[parser->pos] <= 57) || /* 0-9 */ + (js[parser->pos] >= 65 && js[parser->pos] <= 70) || /* A-F */ + (js[parser->pos] >= 97 && js[parser->pos] <= 102))) { /* a-f */ + parser->pos = start; + return JSMN_ERROR_INVAL; + } + parser->pos++; + } + parser->pos--; + break; + /* Unexpected symbol */ + default: + parser->pos = start; + return JSMN_ERROR_INVAL; + } + } + } + parser->pos = start; + return JSMN_ERROR_PART; +} + +/** + * Parse JSON string and fill tokens. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens) { + int r; + int i; + jsmntok_t *token; + int count = parser->toknext; + + for (; parser->pos < len && js[parser->pos] != '\0'; parser->pos++) { + char c; + jsmntype_t type; + + c = js[parser->pos]; + switch (c) { + case '{': case '[': + count++; + if (tokens == NULL) { + break; + } + token = jsmn_alloc_token(parser, tokens, num_tokens); + if (token == NULL) + return JSMN_ERROR_NOMEM; + if (parser->toksuper != -1) { + tokens[parser->toksuper].size++; +#ifdef JSMN_PARENT_LINKS + token->parent = parser->toksuper; +#endif + } + token->type = (c == '{' ? JSMN_OBJECT : JSMN_ARRAY); + token->start = parser->pos; + parser->toksuper = parser->toknext - 1; + break; + case '}': case ']': + if (tokens == NULL) + break; + type = (c == '}' ? JSMN_OBJECT : JSMN_ARRAY); +#ifdef JSMN_PARENT_LINKS + if (parser->toknext < 1) { + return JSMN_ERROR_INVAL; + } + token = &tokens[parser->toknext - 1]; + for (;;) { + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + token->end = parser->pos + 1; + parser->toksuper = token->parent; + break; + } + if (token->parent == -1) { + break; + } + token = &tokens[token->parent]; + } +#else + for (i = parser->toknext - 1; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + if (token->type != type) { + return JSMN_ERROR_INVAL; + } + parser->toksuper = -1; + token->end = parser->pos + 1; + break; + } + } + /* Error if unmatched closing bracket */ + if (i == -1) return JSMN_ERROR_INVAL; + for (; i >= 0; i--) { + token = &tokens[i]; + if (token->start != -1 && token->end == -1) { + parser->toksuper = i; + break; + } + } +#endif + break; + case '\"': + r = jsmn_parse_string(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + case '\t' : case '\r' : case '\n' : case ' ': + break; + case ':': + parser->toksuper = parser->toknext - 1; + break; + case ',': + if (tokens != NULL && parser->toksuper != -1 && + tokens[parser->toksuper].type != JSMN_ARRAY && + tokens[parser->toksuper].type != JSMN_OBJECT) { +#ifdef JSMN_PARENT_LINKS + parser->toksuper = tokens[parser->toksuper].parent; +#else + for (i = parser->toknext - 1; i >= 0; i--) { + if (tokens[i].type == JSMN_ARRAY || tokens[i].type == JSMN_OBJECT) { + if (tokens[i].start != -1 && tokens[i].end == -1) { + parser->toksuper = i; + break; + } + } + } +#endif + } + break; +#ifdef JSMN_STRICT + /* In strict mode primitives are: numbers and booleans */ + case '-': case '0': case '1' : case '2': case '3' : case '4': + case '5': case '6': case '7' : case '8': case '9': + case 't': case 'f': case 'n' : + /* And they must not be keys of the object */ + if (tokens != NULL && parser->toksuper != -1) { + jsmntok_t *t = &tokens[parser->toksuper]; + if (t->type == JSMN_OBJECT || + (t->type == JSMN_STRING && t->size != 0)) { + return JSMN_ERROR_INVAL; + } + } +#else + /* In non-strict mode every unquoted value is a primitive */ + default: +#endif + r = jsmn_parse_primitive(parser, js, len, tokens, num_tokens); + if (r < 0) return r; + count++; + if (parser->toksuper != -1 && tokens != NULL) + tokens[parser->toksuper].size++; + break; + +#ifdef JSMN_STRICT + /* Unexpected char in strict mode */ + default: + return JSMN_ERROR_INVAL; +#endif + } + } + + if (tokens != NULL) { + for (i = parser->toknext - 1; i >= 0; i--) { + /* Unmatched opened object or array */ + if (tokens[i].start != -1 && tokens[i].end == -1) { + return JSMN_ERROR_PART; + } + } + } + + return count; +} + +/** + * Creates a new parser based over a given buffer with an array of tokens + * available. + */ +void jsmn_init(jsmn_parser *parser) { + parser->pos = 0; + parser->toknext = 0; + parser->toksuper = -1; +} + diff --git a/src/jsmn.h b/src/jsmn.h new file mode 100644 index 00000000..e599dcde --- /dev/null +++ b/src/jsmn.h @@ -0,0 +1,99 @@ +/* +Copyright (c) 2010 Serge A. Zaitsev + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +*/ + + +#ifndef __JSMN_H_ +#define __JSMN_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * JSON type identifier. Basic types are: + * o Object + * o Array + * o String + * o Other primitive: number, boolean (true/false) or null + */ +typedef enum { + JSMN_UNDEFINED = 0, + JSMN_OBJECT = 1, + JSMN_ARRAY = 2, + JSMN_STRING = 3, + JSMN_PRIMITIVE = 4 +} jsmntype_t; + +enum jsmnerr { + /* Not enough tokens were provided */ + JSMN_ERROR_NOMEM = -1, + /* Invalid character inside JSON string */ + JSMN_ERROR_INVAL = -2, + /* The string is not a full JSON packet, more bytes expected */ + JSMN_ERROR_PART = -3 +}; + +/** + * JSON token description. + * @param type type (object, array, string etc.) + * @param start start position in JSON data string + * @param end end position in JSON data string + */ +typedef struct { + jsmntype_t type; + int start; + int end; + int size; +#ifdef JSMN_PARENT_LINKS + int parent; +#endif +} jsmntok_t; + +/** + * JSON parser. Contains an array of token blocks available. Also stores + * the string being parsed now and current position in that string + */ +typedef struct { + unsigned int pos; /* offset in the JSON string */ + unsigned int toknext; /* next token to allocate */ + int toksuper; /* superior token node, e.g parent object or array */ +} jsmn_parser; + +/** + * Create JSON parser over an array of tokens + */ +void jsmn_init(jsmn_parser *parser); + +/** + * Run JSON parser. It parses a JSON data string into and array of tokens, each describing + * a single JSON object. + */ +int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, + jsmntok_t *tokens, unsigned int num_tokens); + +#ifdef __cplusplus +} +#endif + +#endif /* __JSMN_H_ */ diff --git a/src/spm_get_lm.c b/src/spm_get_lm.c index 51a2eeae..0840b823 100644 --- a/src/spm_get_lm.c +++ b/src/spm_get_lm.c @@ -1,12 +1,12 @@ /* - * $Id: spm_get_lm.c 4453 2011-09-02 10:47:25Z guillaume $ + * $Id: spm_get_lm.c 6534 2015-08-24 16:02:56Z guillaume $ * Jesper Andersson */ /**************************************************************** ** ** Routine that identifies which voxels in a list of coordinates - ** that are local maxima, and returns a list of indicies into + ** that are local maxima, and returns a list of indices into ** the coordinate list for those maxima. ** ***************************************************************/ @@ -125,7 +125,7 @@ unsigned int get_maxima(double *vol, iy = ((mwIndex) (list[j+1]+0.1)); iz = ((mwIndex) (list[j+2]+0.1)); - if (get_index(ix,iy,iz,vdim) > 0) + if (get_index(ix,iy,iz,vdim) >= 0) { if (is_maxima(vol,vdim,ix,iy,iz,cc)) { @@ -146,7 +146,7 @@ unsigned int get_maxima(double *vol, void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { - mwIndex i = 0, j = 0, k = 0; + mwIndex i = 0, j = 0, k = 0, m = 0, n = 0; int tmpint = 0; const mwSize *pdim = NULL; mwSize ndim = 0; @@ -154,26 +154,26 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) mwSize ln = 0, lm = 0; mwSize n_lindex = 0; mwIndex *lindex = NULL; + unsigned int conn; double *vol = NULL; double *lp = NULL; double *list = NULL; double *plindex = NULL; - if (nrhs < 2) mexErrMsgTxt("Not enough input arguments."); - if (nrhs > 2) mexErrMsgTxt("Too many input arguments."); - if (nlhs < 1) mexErrMsgTxt("Not enough output arguments"); + if (nrhs < 1) mexErrMsgTxt("Not enough input arguments."); + if (nrhs > 3) mexErrMsgTxt("Too many input arguments."); if (nlhs > 1) mexErrMsgTxt("Too many output arguments."); /* Get binary map. */ if (!mxIsNumeric(prhs[0]) || mxIsComplex(prhs[0]) || mxIsSparse(prhs[0]) || !mxIsDouble(prhs[0])) { - mexErrMsgTxt("spm_get_lm: VOL must be numeric, real, full and double"); + mexErrMsgTxt("Input 'vol' must be numeric, real, full and double."); } ndim = mxGetNumberOfDimensions(prhs[0]); if (ndim != 3 && ndim != 2) { - mexErrMsgTxt("spm_get_lm: VOL must 2- or 3-dimensional"); + mexErrMsgTxt("Input 'vol' must 2- or 3-dimensional."); } pdim = mxGetDimensions(prhs[0]); vdim[0] = pdim[0]; vdim[1] = pdim[1]; @@ -182,46 +182,95 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) /* Get list of coordinates */ - if (!mxIsNumeric(prhs[1]) || mxIsComplex(prhs[1]) || mxIsSparse(prhs[1]) || !mxIsDouble(prhs[1])) + if (nrhs < 2) { - mexErrMsgTxt("spm_get_lm: L must be numeric, real, full and double"); - } - lm = mxGetM(prhs[1]); - ln = mxGetN(prhs[1]); - if (!((lm==3 && ndim==3) || (lm==3 && ndim==2) || (lm==2 && ndim==2))) - { - mexErrMsgTxt("spm_get_lm: L must be 3xn (or 2xn) list of voxel coordinates"); + lm = 3; ln = vdim[0] * vdim[1] * vdim[2]; + list = (double *) mxCalloc(lm*ln,sizeof(double)); + for (k=0,m=0,n=0; k +#include +#include +#include "jsmn.h" +#include "mex.h" + +static int create_struct(char *js, jsmntok_t *tok, mxArray **mx); + +static char * get_string(char *js, int start, int end) { + js[end] = '\0'; + return js + start; +} + +static int primitive(char *js, jsmntok_t *tok, mxArray **mx) { + mxArray *ma = NULL; + int sts; + switch (js[tok->start]) { + case 't' : + *mx = mxCreateLogicalScalar(1); + break; + case 'f' : + *mx = mxCreateLogicalScalar(0); + break; + case 'n' : + *mx = mxCreateDoubleScalar(mxGetNaN()); + break; + default: /* '-', '0'..'9' */ + ma = mxCreateString(get_string(js, tok->start, tok->end)); + sts = mexCallMATLAB(1, mx, 1, &ma, "str2double"); + if (sts != 0) { + mexErrMsgTxt("Conversion from string to double failed."); + } + mxDestroyArray(ma); + break; + } + return 1; +} + +static int value(char *js, jsmntok_t *tok, mxArray **mx) { + *mx = mxCreateString(get_string(js, tok->start, tok->end)); + return 1; +} + +static int array(char *js, jsmntok_t *tok, mxArray **mx) { + int i, j; + mxArray *ma = NULL; + *mx = mxCreateCellMatrix(tok->size, 1); + for (i = 0; i < tok->size; i++) { + j += create_struct(js, tok+1+j, &ma); + mxSetCell(*mx, i, ma); + } + /* Call cell2mat if all cell elements are primitives (mxIsNumeric(ma))? */ + return j+1; +} + +static int object(char *js, jsmntok_t *tok, mxArray **mx) { + int i, j, k; + mxArray *ma = NULL; + char *field = NULL; + for (i = 0, j = 0; i < tok->size; i++) { + field = get_string(js, (tok+1+j)->start, (tok+1+j)->end); /* check it is a JSMN_STRING */ + j++; + if (i == 0) { + *mx = mxCreateStructMatrix(1, 1, 1, (const char**)&field); + } + else { + k = mxAddField(*mx, field); + if (k == -1) + mexErrMsgTxt("mxAddField()"); + } + j += create_struct(js, tok+1+j, &ma); + mxSetFieldByNumber(*mx, 0, i, ma); + } + return j+1; +} + +static int create_struct(char *js, jsmntok_t *tok, mxArray **mx) { + if (tok->type == JSMN_PRIMITIVE) { + return primitive(js, tok, mx); + } else if (tok->type == JSMN_STRING) { + return value(js, tok, mx); + } else if (tok->type == JSMN_OBJECT) { + return object(js, tok, mx); + } else if (tok->type == JSMN_ARRAY) { + return array(js, tok, mx); + } + return 0; +} + +static jsmntok_t * parse(const char *js, size_t jslen) { + int r; + jsmn_parser p; + jsmntok_t *tok = NULL; + size_t tokcount = 2; + + jsmn_init(&p); + tok = mxMalloc(sizeof(*tok) * tokcount); + if (tok == NULL) { + mexErrMsgTxt("mxMalloc()"); + } + + for (;;) { + r = jsmn_parse(&p, js, jslen, tok, tokcount); + if (r < 0) { + if (r == JSMN_ERROR_NOMEM) { + tokcount = tokcount * 2; + tok = mxRealloc(tok, sizeof(*tok) * tokcount); + if (tok == NULL) { + mexErrMsgTxt("mxRealloc()"); + } + } + else if ((r == JSMN_ERROR_INVAL) || (r == JSMN_ERROR_PART)) { + mexErrMsgTxt("Invalid or incomplete JSON."); + } + else { + mexErrMsgTxt("Unknown JSON parsing error."); + } + } + else { + break; + } + } + + return tok; +} + +static char * get_data(const mxArray * mx, size_t * jslen) { + /* should attempt to minimise copy */ + int i, filename, sts; + mxArray *ma = NULL; + char *js = NULL; + + js = mxArrayToString(mx); + if (js == NULL) { + mexErrMsgTxt("mxArrayToString()"); + } + *jslen = strlen(js); + + /* detect whether input string is a filename */ + for (i = 0, filename = 1; i < *jslen; i++) { + if ((js[i] == '{') || (js[i] == '[')) { + filename = 0; + break; + } + } + if (filename == 1) { + mxFree(js); + sts = mexCallMATLAB(1, &ma, 1, (mxArray **)&mx, "fileread"); + if (sts != 0) { + mexErrMsgTxt("Cannot read JSON file."); + } + js = mxArrayToString(ma); + if (js == NULL) { + mexErrMsgTxt("mxArrayToString()"); + } + mxDestroyArray(ma); + *jslen = strlen(js); + } + return js; +} + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) { + char *js = NULL; + size_t jslen = 0; + jsmntok_t *tok = NULL; + + /* Validate input arguments */ + if (nrhs == 0) { + mexErrMsgTxt("Not enough input arguments."); + } + else if (nrhs > 1) { + mexErrMsgTxt("Too many input arguments."); + } + if (!mxIsChar(prhs[0])) { + mexErrMsgTxt("Input must be a string."); + } + + /* Get JSON data as char array */ + js = get_data(prhs[0], &jslen); + + /* Parse JSON data */ + tok = parse(js, jslen); + + /* Create output structure */ + create_struct(js, tok, &plhs[0]); + + mxFree(js); + mxFree(tok); +} diff --git a/src/spm_krutil.c b/src/spm_krutil.c index a4befe39..bf57ca70 100644 --- a/src/spm_krutil.c +++ b/src/spm_krutil.c @@ -1,5 +1,5 @@ /* - * $Id: spm_krutil.c 4453 2011-09-02 10:47:25Z guillaume $ + * $Id: spm_krutil.c 6654 2015-12-22 12:55:36Z spm $ * John Ashburner & Jesper Andersson */ @@ -98,7 +98,7 @@ void mexFunction_kron(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[] if (nrhs == 0) mexErrMsgTxt("usage: BM=spm_kron(M1,M2)"); if (nrhs != 2) mexErrMsgTxt("spm_kron: 2 input arguments required"); - if (nlhs != 1) mexErrMsgTxt("spm_kron: 1 output argument required"); + if (nlhs > 1) mexErrMsgTxt("spm_kron: 1 output argument required"); /* Get first matrix */ if (!mxIsNumeric(prhs[0]) || mxIsComplex(prhs[0]) || mxIsSparse(prhs[0]) || !mxIsDouble(prhs[0])) diff --git a/src/spm_mapping.c b/src/spm_mapping.c index 7b9cbe4a..e22b35ec 100644 --- a/src/spm_mapping.c +++ b/src/spm_mapping.c @@ -1,9 +1,9 @@ /* - * $Id: spm_mapping.c 5783 2013-12-05 16:45:55Z guillaume $ + * $Id: spm_mapping.c 6549 2015-09-11 15:37:48Z guillaume $ * John Ashburner */ -/* Matlab dependent high level data access and map manipulation routines */ +/* MATLAB dependent high level data access and map manipulation routines */ #define _FILE_OFFSET_BITS 64 @@ -133,15 +133,23 @@ static void get_map_dat(int i, const mxArray *ptr, MAPTYPE *maps) mexErrMsgTxt("Wrong sized dt."); } pr = mxGetPr(tmp); - if (dtype != (int)fabs(pr[0])) + if (!((pr[0] == 2) && (dtype == SPM_UNSIGNED_CHAR) || + (pr[0] == 256) && (dtype == SPM_SIGNED_CHAR) || + (pr[0] == 4) && (dtype == SPM_SIGNED_SHORT) || + (pr[0] == 512) && (dtype == SPM_UNSIGNED_SHORT) || + (pr[0] == 8) && (dtype == SPM_SIGNED_INT) || + (pr[0] == 768) && (dtype == SPM_UNSIGNED_INT) || + (pr[0] == 16) && (dtype == SPM_FLOAT) || + (pr[0] == 64) && (dtype == SPM_DOUBLE))) { + mexPrintf("dtype=%d and dt=%d\n",dtype,(int)fabs(pr[0])); free_maps(maps,i); mexErrMsgTxt("Incompatible datatype in dt."); } } - maps[i].addr = 0; - maps[i].len = 0; + maps[i].addr = 0; + maps[i].len = 0; maps[i].dtype = dtype; maps[i].data = (void **)mxCalloc(maps[i].dim[2],sizeof(void *)); maps[i].scale = (double *)mxCalloc(maps[i].dim[2],sizeof(double)); @@ -235,7 +243,6 @@ static void get_map_file(int i, const mxArray *ptr, MAPTYPE *maps) maps[i].dim[0] = (int)fabs(pr[0]); maps[i].dim[1] = (int)fabs(pr[1]); maps[i].dim[2] = (int)fabs(pr[2]); - maps[i].dtype = (int)fabs(pr[3]); maps[i].data = (void **)mxCalloc(maps[i].dim[2],sizeof(void *)); maps[i].scale = (double *)mxCalloc(maps[i].dim[2],sizeof(double)); maps[i].offset = (double *)mxCalloc(maps[i].dim[2],sizeof(double)); diff --git a/src/spm_vol_access.c b/src/spm_vol_access.c index b164b529..c0495f6f 100644 --- a/src/spm_vol_access.c +++ b/src/spm_vol_access.c @@ -1,15 +1,19 @@ /* - * $Id: spm_vol_access.c 4453 2011-09-02 10:47:25Z guillaume $ + * $Id: spm_vol_access.c 6552 2015-09-11 16:47:52Z spm $ * John Ashburner */ -/* matlab independent image access routines; -gateway to routines compiled from spm_vol_utils.c */ +/* MATLAB independent image access routines + * Gateway to routines compiled from spm_vol_utils.c + */ #include #include #include "spm_vol_access.h" #include "spm_datatypes.h" +#ifdef SPM_WIN32 +#include +#endif int get_datasize(int type) { @@ -40,6 +44,11 @@ int resample(int m, MAPTYPE *vol, double *out, double *x, double *y, double *z, extern void resample_float_s(int,void**,double*,double*,double*,double*,int,int,int,int,double,double*,double*); extern void resample_double_s(int,void**,double*,double*,double*,double*,int,int,int,int,double,double*,double*); +#ifdef SPM_WIN32 + /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa366801.aspx */ + __try + { +#endif if (vol->dtype == SPM_UNSIGNED_CHAR) resample_uchar(m,vol->data,out,x,y,z,vol->dim[0],vol->dim[1],vol->dim[2], hold, background, vol->scale,vol->offset); @@ -87,6 +96,15 @@ int resample(int m, MAPTYPE *vol, double *out, double *x, double *y, double *z, (void)fprintf(stderr,"%d: Unknown datatype.\n", vol->dtype); return(1); } +#ifdef SPM_WIN32 + } + __except(GetExceptionCode()==EXCEPTION_IN_PAGE_ERROR ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + (void)fprintf(stderr,"An exception occured while accessing the data."); + return(1); + } +#endif return(0); } @@ -107,6 +125,11 @@ int resample_d(int m, MAPTYPE *vol, double *out, double *gradx, double *grady, d extern void resample_d_float_s(int,void**,double*,double*,double*,double*,double*,double*,double*,int,int,int,int,double,double*,double*); extern void resample_d_double_s(int,void**,double*,double*,double*,double*,double*,double*,double*,int,int,int,int,double,double*,double*); +#ifdef SPM_WIN32 + /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa366801.aspx */ + __try + { +#endif if (vol->dtype == SPM_UNSIGNED_CHAR) resample_d_uchar(m,vol->data,out,gradx,grady,gradz,x,y,z,vol->dim[0],vol->dim[1],vol->dim[2], hold, background, vol->scale,vol->offset); @@ -154,6 +177,15 @@ int resample_d(int m, MAPTYPE *vol, double *out, double *gradx, double *grady, d (void)fprintf(stderr,"%d: Unknown datatype.\n", vol->dtype); return(1); } +#ifdef SPM_WIN32 + } + __except(GetExceptionCode()==EXCEPTION_IN_PAGE_ERROR ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + (void)fprintf(stderr,"An exception occured while accessing the data."); + return(1); + } +#endif return(0); } @@ -175,6 +207,11 @@ int slice(double *mat, double *image, int xdim1, int ydim1, MAPTYPE *vol, int ho extern int slice_double_s(double *, double *, int, int, void **, int, int, int, int, double, double*, double *); int sts = 1; +#ifdef SPM_WIN32 + /* https://msdn.microsoft.com/en-us/library/windows/desktop/aa366801.aspx */ + __try + { +#endif if (vol->dtype == SPM_UNSIGNED_CHAR) sts = slice_uchar(mat, image, xdim1,ydim1, vol->data, vol->dim[0],vol->dim[1],vol->dim[2], hold,background, vol->scale,vol->offset); @@ -222,5 +259,14 @@ int slice(double *mat, double *image, int xdim1, int ydim1, MAPTYPE *vol, int ho (void)fprintf(stderr,"%d: Unknown datatype.\n", vol->dtype); sts = 1; } +#ifdef SPM_WIN32 + } + __except(GetExceptionCode()==EXCEPTION_IN_PAGE_ERROR ? + EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) + { + (void)fprintf(stderr,"An exception occured while accessing the data."); + sts = 1; + } +#endif return(sts); } diff --git a/tests/DEMO_BAYES_FACTORS.m b/tests/DEMO_BAYES_FACTORS.m index ff17eff8..2c57918f 100644 --- a/tests/DEMO_BAYES_FACTORS.m +++ b/tests/DEMO_BAYES_FACTORS.m @@ -23,7 +23,7 @@ function DEMO_BAYES_FACTORS(pC,hE,hC,N,b) % Copyright (C) 2010-2014 Wellcome Trust Centre for Neuroimaging % Karl Friston, Peter Zeidman -% $Id: DEMO_BAYES_FACTORS.m 6466 2015-06-03 12:42:14Z karl $ +% $Id: DEMO_BAYES_FACTORS.m 6473 2015-06-04 19:05:05Z karl $ % set up @@ -98,7 +98,7 @@ function DEMO_BAYES_FACTORS(pC,hE,hC,N,b) subplot(2,2,1) hist(F,32), hold on -xlabel('Log Bayes Factor'), ylabel('Frequency') +xlabel('Log Bayes Factor','FontSize',16), ylabel('Frequency') title('Null distribution','FontSize',16) axis square @@ -106,7 +106,7 @@ function DEMO_BAYES_FACTORS(pC,hE,hC,N,b) hist(T,32), hold on plot([u u],[0 Ns/4],'--r'), hold on plot([r r],[0 Ns/4],'--b'), hold off -xlabel('Classical F-ratio'), ylabel('Frequency') +xlabel('Classical F-ratio','FontSize',16), ylabel('Frequency') title('Null distribution','FontSize',16) axis square @@ -117,7 +117,7 @@ function DEMO_BAYES_FACTORS(pC,hE,hC,N,b) plot([3 3],[0 16],':r'), hold on plot([0 0],[0 16],'--r'), hold on plot([-32, 32],[r r],':b'), hold off -xlabel('free energy difference'), ylabel('Classical F-ratio') +xlabel('free energy difference','FontSize',16), ylabel('Classical F-ratio') title('Null distribution','FontSize',16) axis([-8 8 0 16]) axis square diff --git a/tests/DEMO_BMR_PEB.m b/tests/DEMO_BMR_PEB.m index 3e3e1714..8ef81b93 100644 --- a/tests/DEMO_BMR_PEB.m +++ b/tests/DEMO_BMR_PEB.m @@ -34,7 +34,7 @@ % Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging % Karl Friston, Peter Zeidman -% $Id: DEMO_BMR_PEB.m 6449 2015-05-24 14:26:59Z karl $ +% $Id: DEMO_BMR_PEB.m 6557 2015-09-20 12:44:30Z karl $ % set up @@ -195,18 +195,14 @@ %-------------------------------------------------------------------------- M = struct('X',X); -% randomisation analysis +% BMC - (first and second level) (with optimisation of hyperprior) %-------------------------------------------------------------------------- -spm_dcm_peb_rnd(RCM(:,mw),M,{'B'}); - -% BMC - (second level) -%-------------------------------------------------------------------------- -BMC = spm_dcm_bmc_peb(RCM,M,{'B'}); +[BMC,M] = spm_dcm_peb_test(RCM(:,1),M,{'B'}); % BMA - (second level) %-------------------------------------------------------------------------- -PEB = spm_dcm_peb(RCM(:,1),M); -BMA = spm_dcm_peb_bmc(PEB,RCM(1,:)); +PEB = spm_dcm_peb(RCM(:,1),M); +BMA = spm_dcm_peb_bmc(PEB,RCM(1,:)); % posterior predictive density and cross validation @@ -362,7 +358,7 @@ title('Estimated log precision','FontSize',16) axis square, a = axis; -subplot(2,2,2), bar(log((C - 1)/16)) +subplot(2,2,2), bar(log((C - 0)/16)) title('True log precision','FontSize',16) box off, axis square, axis(a) diff --git a/tests/DEMO_Lindley_paradox.m b/tests/DEMO_Lindley_paradox.m new file mode 100644 index 00000000..14124e5d --- /dev/null +++ b/tests/DEMO_Lindley_paradox.m @@ -0,0 +1,165 @@ +function DEMO_Lindley_paradox(pC,hE,hC) +% FORMAT DEMO_BAYES_FACTORS(pC,hE,hC) +% Demonstration Bayes factors and classical p-values +%-------------------------------------------------------------------------- +% pC - prior covariance (e.g., 4) +% hE - expectation of log precision (e.g., 1) +% hC - covariance of log precision (e.g., 1/8) +% +% This demonstration routine uses a simple linear model to examine the +% relationship between free energy differences or log Bayes factors and +% classical F statistics. +%__________________________________________________________________________ +% Copyright (C) 2010-2014 Wellcome Trust Centre for Neuroimaging + +% Karl Friston, Peter Zeidman +% $Id: DEMO_Lindley_paradox.m 6481 2015-06-16 17:01:47Z karl $ + + +% set up +%-------------------------------------------------------------------------- +% rng('default') + +try, pC; catch, pC = 1/4; end +try, hE; catch, hE = 0; end +try, hC; catch, hC = 1/8; end + +sigma_a = 1/8; % weak effect size – alternative hypothesis +sigma_r = 1/64; % reduced (null) effect size +epsilon = 1/16; + +% Model specification +%========================================================================== +M.nograph = 1; +M.noprint = 1; + +M.IS = @(P,M,U) U*P; +M.pE = [0; 0]; +M.pC = eye(2,2)*pC; +M.hE = hE; +M.hC = hC; + +% re-randomisation +%-------------------------------------------------------------------------- +Ns = 1e4; +pE = M.pE; % full prior expectations +pC = M.pC; % full prior covariance +rC = pC; % restricted or reduced priors +rC(1,1) = sigma_r^2; + + +k = (1:Ns) < Ns/2; % null and alternative (prevalence) +N = 2.^(3:10); % umber of subjects +for n = 1:length(N) + + % design matrix and contrast + %-------------------------------------------------------------------------- + X = [randn(N(n),1) ones(N(n),1)]; + for i = 1:Ns + + % effect size - alternative or null + %------------------------------------------------------------------ + if k(i); b = 0; else, b = sigma_a; end + + % generate data + %------------------------------------------------------------------ + beta = [rand(1)*b; 0]; + Y = X*beta + randn(N(n),1); + + % Bayesian analysis (full comparison and model reduction) + %------------------------------------------------------------------ + [qE,qC] = spm_nlsi_GN(M,X,Y); + F(i,1) = -spm_log_evidence(qE,qC,pE,pC,pE,rC); + QE(i,1) = qE(1); + QC(i,1) = qC(1); + + % classical analysis + %------------------------------------------------------------------ + [t,df,qE] = spm_ancova(X,[],Y,[1 0;0 0]); + T(i,1) = t; + qe(i,1) = qE(1); + P(i,1) = beta(1); + + end + + + % classical threshold and PPV + %---------------------------------------------------------------------- + u = spm_invFcdf(0.95,df); + i = find(P > 0); + j = find(P <= 0); + + FPR(n) = sum(T(j) > u)/length(j); + PPV(n) = sum(T(i) > u)/sum(T > u); + + i = find(P > epsilon); + j = find(P <= epsilon); + + EPR(n) = sum(T(j) > u)/length(j); + EPV(n) = sum(T(i) > u)/sum(T > u); + + fpr(n) = sum(F(j) > 0)/length(j); + ppv(n) = sum(F(i) > 0)/sum(F > 0); + + i = find(T > u); + Ep(n) = mean(P(i)); + Cp(n) = var(P(i)); + i = find(F > 0); + ep(n) = mean(P(i)); + cp(n) = var(P(i)); + + +end + + + + +% show results +%========================================================================== +spm_figure('GetWin','Graphics_null');clf + +subplot(2,2,1) +plot(N,(N*0 + 0.05),'--r'), hold on +plot(N,(N*0 + 0.80),'--b'), hold on +plot(N,FPR, 'r',N,fpr,'--r'), hold on +plot(N,EPR,'g',N,EPV,'g'), hold on +plot(N,PPV, 'b',N,ppv,'--b'), hold off +xlabel('Number of samples'), ylabel('Probability') +title('PPV and FPR','FontSize',16) +axis([0 N(end) 0 1]); axis square + +subplot(2,2,3) +spm_plot_ci(Ep,Cp,N), hold on +plot(N,N - N + sigma_r,'--'), hold on +plot(N,N - N + sigma_a,'--r'), hold off +xlabel('Number of samples'), ylabel('effect science') +title('Detected effect size','FontSize',16) +axis square, axis([0 N(end) 0 1]); axis square + +subplot(2,2,4) +spm_plot_ci(ep,cp,N), hold on +plot(N,N - N + sigma_r,'--'), hold on +plot(N,N - N + sigma_a,'--r'), hold off +xlabel('Number of samples'), ylabel('effect science') +title('Bayesian','FontSize',16) +axis square, axis([0 N(end) 0 1]); axis square + + +% (linear) mapping between free energy difference and F ratio +%-------------------------------------------------------------------------- +j = abs(F) < 32; +b = pinv([F(j) ones(size(F(j)))])*T(j); +Fq = (-32:32)'; +Tq = [Fq, ones(size(Fq))]*b; + +subplot(2,2,2) +plot(F,T,'.b','Markersize',8), hold on +plot(Fq,Tq,'b'), hold on +plot([3 3],[0 16],':r'), hold on +plot([0 0],[0 16],'--r'), hold on +plot([-32, 32],[u u],':k'), hold off +xlabel('Free energy difference'), ylabel('Classical F-ratio') +title('Classical and Bayesian statistics','FontSize',16) +axis([-8 8 0 16]) +axis square + diff --git a/tests/ROBOT_DCM_EEG.m b/tests/ROBOT_DCM_EEG.m index ae25562f..d5f78211 100644 --- a/tests/ROBOT_DCM_EEG.m +++ b/tests/ROBOT_DCM_EEG.m @@ -5,7 +5,7 @@ % options.model - 'ERP','SEP','CMC','LFP','NNM' or 'MFM' % options.spatial - 'ECD','LFP' or 'IMG' -% $Id: ROBOT_DCM_EEG.m 6360 2015-03-04 19:24:56Z spm $ +% $Id: ROBOT_DCM_EEG.m 6557 2015-09-20 12:44:30Z karl $ % tests of spatial models: 'ECD', 'LFP' or 'IMG' %========================================================================== @@ -164,7 +164,6 @@ if isfield(DCM,'M') DCM = rmfield(DCM,'M'); end - DCM.M.Nmax = 1; DCM = spm_dcm_csd(DCM); spm_figure('GetWin',['CSD model: ' model{i}]); spm_dcm_csd_results(DCM,'Cross-spectra (channels)',gcf) diff --git a/tests/test_gifti.m b/tests/test_gifti.m new file mode 100644 index 00000000..64b88ca4 --- /dev/null +++ b/tests/test_gifti.m @@ -0,0 +1,138 @@ +function tests = test_gifti +% Unit Tests for gifti +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% $Id: test_gifti.m 6516 2015-08-07 17:28:33Z guillaume $ + +tests = functiontests(localfunctions); + + +function test_gifti_constructor(testCase) +import matlab.unittest.constraints.* +g1 = gifti; +g2 = gifti(fullfile(spm('Dir'),'canonical','cortex_20484.surf.gii')); +g3 = gifti(g2); +s = struct(g3); +g4 = gifti(s); +g5 = gifti(rand(16,1)); + +testCase.verifyThat(g1, IsOfClass('gifti')); +testCase.verifyThat(fieldnames(g1), IsEmpty); + +testCase.verifyThat(g2, IsOfClass('gifti')); +testCase.verifyThat(s, HasField('faces')); +testCase.verifyThat(s, HasField('vertices')); +testCase.verifyThat(s, HasField('mat')); + +testCase.verifyThat(g3, IsOfClass('gifti')); +testCase.verifyThat(g3, IsEqualTo(g2)); + +testCase.verifyThat(s, IsOfClass('struct')); +testCase.verifyThat(s, HasField('faces')); +testCase.verifyThat(s, HasField('vertices')); +testCase.verifyThat(s, HasField('mat')); + +testCase.verifyThat(g4, IsOfClass('gifti')); +testCase.verifyThat(struct(g4), IsEqualTo(s)); + +testCase.verifyThat(g5, IsOfClass('gifti')); +testCase.verifyThat(g5, HasField('cdata')); + + +function test_gifti_accessor(testCase) +import matlab.unittest.constraints.* +g1 = gifti(fullfile(spm('Dir'),'canonical','cortex_8196.surf.gii')); +s.faces = g1.faces; +s.vertices = g1.vertices; +s.mat = g1.mat; +testCase.verifyEqual(s.faces(1000,:), g1.faces(1000,:)); +testCase.verifyEqual(s.vertices(1000,:), g1.vertices(1000,:)); +testCase.verifyEqual(s.mat(:,4), g1.mat(:,4)); + +cdata = single(rand(16,1)); +g2 = gifti(cdata); +testCase.verifyEqual(cdata,g2.cdata); +testCase.verifyEqual(cdata(8),g2.cdata(8)); +testCase.verifyEqual(cdata(8,1),g2.cdata(8,1)); + + +function test_gifti_mutator(testCase) +import matlab.unittest.constraints.* +g = gifti(fullfile(spm('Dir'),'canonical','cortex_5124.surf.gii')); +g.mat = eye(4); +g.mat(logical(eye(4))) = 2; +g.mat(4,4) = 1; +testCase.verifyEqual(g.mat, diag([2 2 2 1])); + +faces = g.faces; +faces = faces(:,[2 1 3]); +g.faces = faces; +g.faces(64,:) = [1 2 3]; +g.faces(:,1) = faces(:,1); +testCase.verifyEqual(g.faces(64,:), [faces(64,1) 2 3]); + +vertices = g.vertices; +vertices = vertices(:,[2 1 3]); +g.vertices = vertices; +g.vertices(64,:) = zeros(1,3); +g.vertices(:,2:3) = ones(size(vertices,1),2); +testCase.verifyEqual(g.vertices(64,:), single([0 1 1])); + +g.cdata = rand(size(g.vertices,1),1); +g.cdata(1) = pi; +g.cdata(2:end) = exp(1); +testCase.verifyEqual(g.cdata(1), single(pi)); + + +function test_gifti_export(testCase) +import matlab.unittest.constraints.* +mri = load('mri'); +g = gifti(isosurface(smooth3(squeeze(mri.D)),5)); +s = export(g); +testCase.verifyThat(s, HasField('vertices')); +testCase.verifyThat(s, HasField('faces')); +s = export(g,'matlab'); +testCase.verifyThat(s, HasField('vertices')); +testCase.verifyThat(s, HasField('faces')); +s = export(g,'patch'); +testCase.verifyThat(s, HasField('vertices')); +testCase.verifyThat(s, HasField('faces')); +testCase.verifyThat(s, ~HasField('mat')); +s = export(g,'fieldtrip'); +testCase.verifyThat(s, HasField('pnt')); +testCase.verifyThat(s, HasField('tri')); +s = export(g,'spm'); +testCase.verifyThat(s, HasField('vert')); +testCase.verifyThat(s, HasField('face')); + + +function test_gifti_load(testCase) +import matlab.unittest.constraints.* +d = fullfile(spm('Dir'),'canonical'); +files = dir(fullfile(d,'*.gii')); +for i=1:numel(files) + g = gifti(fullfile(d,files(i).name)); + testCase.verifyThat(evalc('g'), ~IsEmpty); % check display() +end + + +function test_gifti_save(testCase) +mri = load('mri'); +g = gifti(isosurface(smooth3(squeeze(mri.D)),5)); +g.cdata = rand(size(g.vertices,1),1); +basename = tempname; +file = [basename '.gii']; +save(g,file,'ASCII'); +g = gifti(file); +delete(file); +save(g,file,'Base64Binary'); +g = gifti(file); +delete(file); +save(g,file,'GZipBase64Binary'); +g = gifti(file); +delete(file); +save(g,file,'ExternalFileBinary'); +g = gifti(file); +delete(file); +delete([basename '.dat']); diff --git a/tests/test_jsonread.m b/tests/test_jsonread.m new file mode 100644 index 00000000..909bc3fa --- /dev/null +++ b/tests/test_jsonread.m @@ -0,0 +1,14 @@ +function tests = test_jsonread +% Unit Tests for jsonread +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% $Id: test_jsonread.m 6589 2015-11-03 16:01:08Z guillaume $ + +tests = functiontests(localfunctions); + + +function test_jsonread_from_string(testCase) +exp = struct('name','Karl','age',56); +act = spm_jsonread('{ "name" : "Karl", "age" : 56 }'); +testCase.verifyTrue(isequal(exp, act)); diff --git a/tests/test_spm_BMS_gibbs.m b/tests/test_spm_BMS_gibbs.m new file mode 100644 index 00000000..886426cf --- /dev/null +++ b/tests/test_spm_BMS_gibbs.m @@ -0,0 +1,53 @@ +function tests = test_spm_BMS_gibbs +% Unit Tests for spm_BMS_gibbs +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% $Id: test_spm_BMS_gibbs.m 6492 2015-06-26 14:27:40Z guillaume $ + +tests = functiontests(localfunctions); + + +function test_strong_evidence(testCase) + +% Setup +import matlab.unittest.constraints.* +rng('default'); +rng(1); + +n = 20; % Subjects +models = 4; % Models per subject + +% Frequency of each model in the group +r = [0.60 0.20 0.15 0.05]; + +num_subjects_per_model = n .* r; + +% Build subjects x models matrix of model assignments +m = []; +for model = 1:models + row = zeros(1,models); + row(model) = 1; + + m = [m; repmat(row, num_subjects_per_model(model), 1)]; +end + +% Free energy of the generative model in each subject +mean_F = -10; +std_F = 0.5; +F_gen = mean_F + std_F .* randn(sum(m(:) > 0),1); + +% Build free energy matrix (non-generative models: mean -13, STD 1) +F = -13 + randn(n,4); +F(m > 0) = F_gen; + +% Run +[exp_r,xp] = spm_BMS_gibbs(F); + +% Check expected frequencies are within 10% +actual = exp_r; +testCase.verifyThat(actual, IsEqualTo(r, 'Within', AbsoluteTolerance(0.1) ) ); + +% Check exceedance probability +actual = xp(1); +testCase.verifyThat(actual, IsGreaterThanOrEqualTo(0.95) ); diff --git a/tests/test_spm_dcm_post_hoc.m b/tests/test_spm_dcm_post_hoc.m new file mode 100644 index 00000000..ddfb6ece --- /dev/null +++ b/tests/test_spm_dcm_post_hoc.m @@ -0,0 +1,76 @@ +function tests = test_spm_dcm_post_hoc +% Unit Tests for spm_dcm_post_hoc +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% $Id: test_spm_dcm_post_hoc.m 6492 2015-06-26 14:27:40Z guillaume $ + +tests = functiontests(localfunctions); + + +%-------------------------------------------------------------------------- +function data_path = get_data_path() +data_path = fullfile( spm('Dir'), 'tests', ... + 'data', 'test_spm_dcm_post_hoc'); + + +%-------------------------------------------------------------------------- +function setup(testCase) +data_path = get_data_path(); + +% Expected outputs +artefacts = {fullfile(data_path,'DCM_BPA.mat'); + fullfile(data_path,'DCM_opt_fwd_bwd_simulated.mat')}; + +% Delete if exist +for i = 1:length(artefacts) + spm_unlink(artefacts{i}); +end + +% Initialize SPM +spm('defaults','fmri'); +spm_get_defaults('cmdline',true); + + +%-------------------------------------------------------------------------- +function test_on_simulated_attention_data(testCase) +import matlab.unittest.constraints.* + +data_path = get_data_path(); + +% Load model matching the generative model +DCM_fwd = load(fullfile(data_path, 'DCM_fwd_simulated.mat')); +DCM_fwd = DCM_fwd.DCM; + +% Load model with extra connection +DCM_fwd_bwd = load(fullfile(data_path, 'DCM_fwd_bwd_simulated.mat')); +DCM_fwd_bwd = DCM_fwd_bwd.DCM; + +% Run post-hoc +DCM_BPA = spm_dcm_post_hoc(fullfile(data_path, 'DCM_fwd_bwd_simulated.mat')); +DCM_opt = load(fullfile(data_path,'DCM_opt_fwd_bwd_simulated.mat')); +DCM_opt = DCM_opt.DCM; + +% Check artefacts were created +testCase.assertEqual( exist(fullfile(data_path,'DCM_BPA.mat'),'file'), 2 ); +testCase.assertEqual( exist(fullfile(data_path,'DCM_opt_fwd_bwd_simulated.mat'),'file'), 2 ); + +% Check reduced priors match the generative model +pC_fwd = full(diag(DCM_fwd.M.pC)); +pC_opt = full(diag(DCM_opt.M.pC)); +testCase.assertTrue(all(pC_fwd == pC_opt)); + +% % Plot for visual inspection +% spm_figure('Getwin','BMC'); clf +% +% subplot(2,2,1); spm_plot_ci(DCM_fwd.Ep,DCM_fwd.Cp); +% title('Fwd (generative) model'); xlabel('Parameter') +% +% subplot(2,2,2); spm_plot_ci(DCM_fwd_bwd.Ep,DCM_fwd_bwd.Cp); +% title('Bwd (over-complex) model'); xlabel('Parameter') +% +% subplot(2,2,3); spm_plot_ci(DCM_BPA.Ep,DCM_BPA.Cp); +% title('BPA'); xlabel('Parameter') +% +% subplot(2,2,4); spm_plot_ci(DCM_opt.Ep,DCM_opt.Cp); +% title('Optimal model for subject'); xlabel('Parameter'); \ No newline at end of file diff --git a/tests/test_spm_get_lm.m b/tests/test_spm_get_lm.m new file mode 100644 index 00000000..b45f86a1 --- /dev/null +++ b/tests/test_spm_get_lm.m @@ -0,0 +1,30 @@ +function tests = test_spm_get_lm +% Unit Tests for spm_get_lm +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% $Id: test_spm_get_lm.m 6534 2015-08-24 16:02:56Z guillaume $ + +tests = functiontests(localfunctions); + + +function test_spm_get_lm_2D(testCase) +dim = [5 5]; +vol = rand(dim(1),dim(2)); +subvol = vol(2:4,2:4); +vol(3,3) = max(subvol(:)) + 0.1; +[X,Y] = ndgrid(1:dim(1),1:dim(2)); +gm = sub2ind(size(vol),3,3); +idx = spm_get_lm(vol,[X(:)';Y(:)']); +testCase.verifyTrue(any(idx == gm)); + + +function test_spm_get_lm_3D(testCase) +dim = [5 5 5]; +vol = rand(dim(1),dim(2),dim(3)); +subvol = vol(2:4,2:4,2:4); +vol(3,3,3) = max(subvol(:)) + 0.1; +[X,Y,Z] = ndgrid(1:dim(1),1:dim(2),1:dim(3)); +gm = sub2ind(size(vol),3,3,3); +idx = spm_get_lm(vol,[X(:)';Y(:)';Z(:)']); +testCase.verifyTrue(any(idx == gm)); diff --git a/toolbox/DARTEL/tbx_cfg_dartel.m b/toolbox/DARTEL/tbx_cfg_dartel.m index 128b0b59..bac2b382 100644 --- a/toolbox/DARTEL/tbx_cfg_dartel.m +++ b/toolbox/DARTEL/tbx_cfg_dartel.m @@ -4,7 +4,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: tbx_cfg_dartel.m 6050 2014-06-16 18:58:21Z guillaume $ +% $Id: tbx_cfg_dartel.m 6522 2015-08-14 18:55:42Z john $ if ~isdeployed, addpath(fullfile(spm('dir'),'toolbox','DARTEL')); end @@ -752,7 +752,7 @@ images = cfg_files; images.tag = 'images'; images.name = 'Images'; -images.help = {'Select the image(s) to be inverse normalised. These should be in alignment with the template image generated by the warping procedure.'}; +images.help = {'Select the image(s) to be inverse normalised. These should be in alignment with the template image of the warping procedure (Run Dartel).'}; images.filter = 'nifti'; images.ufilter = '.*'; images.num = [1 Inf]; diff --git a/toolbox/DEM/ADEM_eyeblink.m b/toolbox/DEM/ADEM_eyeblink.m index 5c8e6c00..7a426a30 100644 --- a/toolbox/DEM/ADEM_eyeblink.m +++ b/toolbox/DEM/ADEM_eyeblink.m @@ -1,5 +1,7 @@ function DEM = ADEM_eyeblink(OPTION) +% Simulation of eyeblink conditioning % FORMAT DEM = ADEM_eyeblink(OPTION) +% % OPTION: % case{'EYEBLINK'} : spontaneous eye blinking % case{'AIRPUFF'} : unconditioned eyeblink response to air puff @@ -45,7 +47,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: ADEM_eyeblink.m 6378 2015-03-15 14:46:41Z karl $ +% $Id: ADEM_eyeblink.m 6655 2015-12-23 20:21:27Z karl $ % paradigm and stimuli diff --git a/toolbox/DEM/ADEM_salience.m b/toolbox/DEM/ADEM_salience.m index 52aaf8ad..de001f40 100644 --- a/toolbox/DEM/ADEM_salience.m +++ b/toolbox/DEM/ADEM_salience.m @@ -1,5 +1,5 @@ function ADEM_salience -% Saccadic eye movements under active inference: +% Saccadic eye movements under active inference %__________________________________________________________________________ % This demo illustrates exploration or visual search in terms of optimality % principles based on straightforward ergodic or allostatic principles. @@ -17,10 +17,13 @@ % sampling of sensory data) to simulate saccadic eye movements under % active inference. %__________________________________________________________________________ -% Copyright (C) 2011 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2011-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: ADEM_salience.m 5521 2013-05-25 11:55:48Z karl $ +% $Id: ADEM_salience.m 6592 2015-11-06 16:20:48Z guillaume $ + + +pth = fileparts(mfilename('fullpath')); % hidden causes and states @@ -51,17 +54,18 @@ try - STIM.H{1} = spm_vol('face_R.nii'); - STIM.H{2} = spm_vol('face_rot_R.nii'); - STIM.H{3} = spm_vol('face_inv_R.nii'); + STIM.H{1} = spm_vol(fullfile(pth,'face_R.nii')); + STIM.H{2} = spm_vol(fullfile(pth,'face_rot_R.nii')); + STIM.H{3} = spm_vol(fullfile(pth,'face_inv_R.nii')); - STIM.S{1} = spm_vol('face.nii'); - STIM.S{2} = spm_vol('face_rot.nii'); - STIM.S{3} = spm_vol('face_inv.nii'); + STIM.S{1} = spm_vol(fullfile(pth,'face.nii')); + STIM.S{2} = spm_vol(fullfile(pth,'face_rot.nii')); + STIM.S{3} = spm_vol(fullfile(pth,'face_inv.nii')); catch - errordlg('please change current directory to DEM toolbox') + error('Images not found.'); + end @@ -70,13 +74,13 @@ dim = 16; % dimension of visual sample ns = dim*dim; % number of sensory channels nh = length(STIM.H); % number of hypotheses -STIM.R = hanning(dim)*hanning(dim)'; % Retinal precision +STIM.R = spm_hanning(dim)*spm_hanning(dim)'; % Retinal precision -STIM.V = spm_vol('Nefertiti_R.nii'); % Stimulus (filtered) -STIM.U = spm_vol('Nefertiti.nii'); % Stimulus (unfiltered) +STIM.V = spm_vol(fullfile(pth,'Nefertiti_R.nii')); % Stimulus (filtered) +STIM.U = spm_vol(fullfile(pth,'Nefertiti.nii')); % Stimulus (unfiltered) -STIM.V = spm_vol('face_R.nii'); % Stimulus (filtered) -STIM.U = spm_vol('face.nii'); % Stimulus (unfiltered) +STIM.V = spm_vol(fullfile(pth,'face_R.nii')); % Stimulus (filtered) +STIM.U = spm_vol(fullfile(pth,'face.nii')); % Stimulus (unfiltered) % hidden states @@ -210,7 +214,7 @@ % illustrate salience for %========================================================================== nr = 64; -STIM.H{1} = spm_vol('Nefertiti_R.nii'); +STIM.H{1} = spm_vol(fullfile(pth,'Nefertiti_R.nii')); M(1).x.x = 0; S = spm_salience_map(M,nr); @@ -233,7 +237,7 @@ % write volume structure %-------------------------------------------------------------------------- V = struct(... - 'fname', [fname '.nii'],... + 'fname', fullfile(pth,[fname '.nii']),... 'dim', DIM,... 'mat', M,... 'pinfo', [1 0 0]',... @@ -246,8 +250,7 @@ %========================================================================== for i = 1:length(H) - [PATHSTR,NAME,EXT] = fileparts(H{i}.fname); - fname = [NAME '_R' EXT]; + fname = spm_file(H{i}.fname,'suffix','_R'); s = spm_read_vols(H{i}); s = s/max(max(s)); s = (spm_conv(s,1) - spm_conv(s,4)); @@ -261,7 +264,7 @@ 'dim', DIM,... 'mat', M,... 'pinfo', [1 0 0]',... - 'descrip',NAME); + 'descrip',spm_file(fname,'basename')); V = spm_create_vol(V); V = spm_write_plane(V,s,1); diff --git a/toolbox/DEM/DEM_demo.fig b/toolbox/DEM/DEM_demo.fig index 663d7471..787ec5d8 100644 Binary files a/toolbox/DEM/DEM_demo.fig and b/toolbox/DEM/DEM_demo.fig differ diff --git a/toolbox/DEM/DEM_demo.m b/toolbox/DEM/DEM_demo.m index 95b933a4..612fa60e 100644 --- a/toolbox/DEM/DEM_demo.m +++ b/toolbox/DEM/DEM_demo.m @@ -22,7 +22,7 @@ % Edit the above text to modify the response to help DEM_demo -% Last Modified by GUIDE v2.5 28-Jan-2015 14:31:06 +% Last Modified by GUIDE v2.5 07-Jan-2016 18:33:42 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -519,3 +519,33 @@ function pushbutton158_Callback(hObject, eventdata, handles) function pushbutton159_Callback(hObject, eventdata, handles) handles.web = 'http://www.fil.ion.ucl.ac.uk/spm/doc/papers/sjk_aibf.pdf'; run_demo_Callback(hObject, handles, 'DEM_spatial_deconvolution') + +% --- Executes on button press in pushbutton160. +function pushbutton160_Callback(hObject, eventdata, handles) +handles.web = ''; +run_demo_Callback(hObject, handles, 'DEM_demo_ontology') + +% --- Executes on button press in pushbutton161. +function pushbutton161_Callback(hObject, eventdata, handles) +handles.web = 'http://www.fil.ion.ucl.ac.uk/~karl/Active%20inference%20and%20epistemic%20value.pdf'; +run_demo_Callback(hObject, handles, 'DEM_demo_MDP_habits') + +% --- Executes on button press in pushbutton162. +function pushbutton162_Callback(hObject, eventdata, handles) +handles.web = 'http://www.fil.ion.ucl.ac.uk/~karl/Active%20inference%20and%20epistemic%20value.pdf'; +run_demo_Callback(hObject, handles, 'DEM_demo_MDP_fit') + +% --- Executes on button press in pushbutton163. +function pushbutton163_Callback(hObject, eventdata, handles) +handles.web = 'http://www.fil.ion.ucl.ac.uk/~karl/Active%20inference%20and%20epistemic%20value.pdf'; +run_demo_Callback(hObject, handles, 'DEM_demo_MDP_X') + +% --- Executes on button press in pushbutton168. +function pushbutton168_Callback(hObject, eventdata, handles) +handles.web = 'http://www.fil.ion.ucl.ac.uk/~karl/Active%20inference%20and%20epistemic%20value.pdf'; +run_demo_Callback(hObject, handles, 'DEM_demo_MDP_search') + +% --- Executes on button press in pushbutton169. +function pushbutton169_Callback(hObject, eventdata, handles) +handles.web = 'http://www.fil.ion.ucl.ac.uk/~karl/Active%20inference%20and%20epistemic%20value.pdf'; +run_demo_Callback(hObject, handles, 'DEM_demo_MDP_reading') diff --git a/toolbox/DEM/DEM_demo_DCM_LAP.m b/toolbox/DEM/DEM_demo_DCM_LAP.m index c20abbf6..0686637e 100644 --- a/toolbox/DEM/DEM_demo_DCM_LAP.m +++ b/toolbox/DEM/DEM_demo_DCM_LAP.m @@ -12,7 +12,7 @@ % Copyright (C) 2010 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: DEM_demo_DCM_LAP.m 5692 2013-10-13 13:44:05Z karl $ +% $Id: DEM_demo_DCM_LAP.m 6483 2015-06-21 21:14:34Z karl $ % Specify a DCM to generate synthetic data %========================================================================== @@ -22,7 +22,6 @@ % ------------------------------------------------------------------------- T = 256; TR = 3.22; -t = (1:T)*TR; n = 4; U = spm_conv(randn(n,T),0,2)/4; @@ -142,7 +141,15 @@ P(i,1) = spm_log_evidence(qE,qC,pE,pC,rE,rC); end - +% posterior density under best model +% ------------------------------------------------------------------------- +[p,i] = max(P); +k = find(~A{i}); +rE = pE; +rC = pC; +rE(k) = 0; +rC(k,k) = 0; +[F,sE,sC] = spm_log_evidence_reduce(qE,qC,pE,pC,rE,rC); % log-posterior (model) % ------------------------------------------------------------------------- @@ -154,7 +161,7 @@ spm_figure('Getwin','Figure 2'); clf subplot(2,2,1) -spm_plot_ci(spm_vec(qE),qC), hold on +spm_plot_ci(spm_vec(sE),sC), hold on bar(spm_vec(pP.A),1/2), hold off title('true and MAP connections','FontSize',16) axis square diff --git a/toolbox/DEM/DEM_demo_DFP.m b/toolbox/DEM/DEM_demo_DFP.m index beacee2b..b15a2313 100644 --- a/toolbox/DEM/DEM_demo_DFP.m +++ b/toolbox/DEM/DEM_demo_DFP.m @@ -13,15 +13,16 @@ %========================================================================== spm_figure('GetWin','DEM'); -M = spm_DEM_M('convolution model'); -M(1).V = exp(8); -M(1).W = exp(16); +M = spm_DEM_M('convolution model'); +M(1).V = exp(8); +M(1).W = exp(16); +M(1).E.N = 32; % and generate data %========================================================================== N = 32; % length of data sequence -U = exp(-([1:N] - N/4).^2/(2*(N/32)^2)); % Gaussian cause +U = exp(-((1:N) - N/4).^2/(2*(N/32)^2)); % Gaussian cause DEM = spm_DEM_generate(M,U,{},{32 16}); % display diff --git a/toolbox/DEM/DEM_demo_MDP_X.m b/toolbox/DEM/DEM_demo_MDP_X.m new file mode 100644 index 00000000..f4e44431 --- /dev/null +++ b/toolbox/DEM/DEM_demo_MDP_X.m @@ -0,0 +1,183 @@ +function MDP = DEM_demo_MDP_X +% Demo of active inference for trust games +%__________________________________________________________________________ +% +% This routine uses a Markov decision process formulation of active +% inference (with variational Bayes) to model foraging for information in a +% three arm maze. This demo illustrates variational free energy +% minimisation in the context of Markov decision processes, where the agent +% is equipped with prior beliefs that it will minimise expected free energy +% in the future. This free energy is the free energy of future sensory +% states expected under the posterior predictive distribution. It can be +% regarded as a generalisation of the variational formulation of KL control +% in which information gain or epistemic value is formulated explicitly. +% +% In this example, the agent starts at the centre of a three way maze +% which is baited with a reward in one of the two upper arms. However, the +% rewarded arm changes from trial to trial. Crucially, the agent can +% identify where the reward (US) is located by accessing a cue (CS) in the +% lower arm. This tells the agent whether the reward is on the left or the +% right upper arm. This means the optimal policy would first involve +% maximising information gain or epistemic value by moving to the lower arm +% and then claiming the reward this signified. Here, there are eight hidden +% states (four locations times right or left reward), four control states +% (that take the agent to the four locations) and four exteroceptive +% outcomes (that depend on the agents locations) plus three interoceptive +% outcomes indicating reward (or not). +% +% This version focuses on factorising the hidden states causing +% (factorised) outcomes. This factorisation is implicit in the tensor +% production used in the companion demo. Here the factorisation is explicit +% enabling us to model multiple modalities (outcome factors) and distinct +% hidden causes of observation (hidden state factors like what and where). +% The behaviour is formally similar to the vanilla scheme but allows a much +% more intuitive (and possibly flexible) model specification. +% +% see also: DEM_demo_MDP_habits.m and spm_MPD_VB_X.m +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: DEM_demo_MDP_X.m 6657 2015-12-31 17:59:31Z karl $ + +% set up and preliminaries +%========================================================================== +rng('default') + +% outcome probabilities: A +%-------------------------------------------------------------------------- +% We start by specifying the probabilistic mapping from hidden states +% to outcomes; where outcome can be exteroceptive or interoceptive: The +% exteroceptive outcomes A{1} provide cues about location and context, +% while interoceptive outcome A{2) denotes different levels of reward +%-------------------------------------------------------------------------- +a = .90; +b = 1 - a; +A{1}(:,:,1) = [... + 1 0 0 0; % cue start + 0 1 0 0; % cue left + 0 0 1 0; % cue right + 0 0 0 1 % cue CS right + 0 0 0 0]; % cue CS left +A{1}(:,:,2) = [... + 1 0 0 0; % cue start + 0 1 0 0; % cue left + 0 0 1 0; % cue right + 0 0 0 0 % cue CS right + 0 0 0 1]; % cue CS left + +A{2}(:,:,1) = [... + 1 0 0 1; % reward neutral + 0 a b 0; % reward positive + 0 b a 0]; % reward negative +A{2}(:,:,2) = [... + 1 0 0 1; % reward neutral + 0 b a 0; % reward positive + 0 a b 0]; % reward negative + + +% controlled transitions: B{u} +%-------------------------------------------------------------------------- +% Next, we have to specify the probabilistic transitions of hidden states +% for each factor. Here, there are four actions taking the agent directly +% to each of the four locations. +%-------------------------------------------------------------------------- +B{1}(:,:,1) = [1 0 0 1; 0 1 0 0;0 0 1 0;0 0 0 0]; +B{1}(:,:,2) = [0 0 0 0; 1 1 0 1;0 0 1 0;0 0 0 0]; +B{1}(:,:,3) = [0 0 0 0; 0 1 0 0;1 0 1 1;0 0 0 0]; +B{1}(:,:,4) = [0 0 0 0; 0 1 0 0;0 0 1 0;1 0 0 1]; + +% context, which cannot be changed by action +%-------------------------------------------------------------------------- +B{2} = eye(2); + +% priors: (utility) C +%-------------------------------------------------------------------------- +% Finally, we have to specify the prior preferences in terms of log +% probabilities over outcomes. Here, the agent prefers rewards to losses - +% and has no prior preferences about where it is: +%-------------------------------------------------------------------------- +c = 4; +C{1} = [0 0 0; + 0 0 0; + 0 0 0; + 0 0 0; + 0 0 0]; + +C{2} = [ 0 0 0; + c c c; + -c -c -c]; + +% now specify prior beliefs about initial states, in terms of counts. Here +% the hidden states are factorised into location and context: +%-------------------------------------------------------------------------- +d{1} = [1 0 0 0]'; +d{2} = [1 1]'*4; + + +% allowable policies (of depth T). These are just sequences of actions +% (with an action for each hidden factor) +%-------------------------------------------------------------------------- +V(:,:,1) = [1 1 1 1 2 3 4 4 4 4 + 1 2 3 4 2 3 1 2 3 4]; +V(:,:,2) = 1; + + +% MDP Structure - this will be used to generate arrays for multiple trials +%========================================================================== +mdp.V = V; % allowable policies +mdp.A = A; % observation model +mdp.B = B; % transition probabilities +mdp.C = C; % preferred outcomes +mdp.d = d; % prior over initial states +mdp.s = [1 1]'; % true initial state + +mdp.Aname = {'exteroceptive','interoceptive'}; +mdp.Bname = {'position','context'}; + +% true initial states – with context change at trial 12 +%-------------------------------------------------------------------------- +i = [1,3]; % change context in a couple of trials +[MDP(1:32)] = deal(mdp); % create structure array +[MDP(i).s] = deal([1 2]'); % deal context changes + + +% Solve - an example game: a run of reds then an oddball +%========================================================================== +MDP = spm_MDP_VB_X(MDP); + +% illustrate behavioural responses – first trial +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 1'); clf +spm_MDP_VB_trial(MDP(1)); + +% illustrate behavioural responses and neuronal correlates +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 2'); clf +spm_MDP_VB_game(MDP); + +% illustrate phase-precession and responses to chosen option - 1st trial +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 3'); clf +spm_MDP_VB_LFP(MDP(1),[2 3;3 3],1); + +% illustrate phase-amplitude (theta-gamma) coupling +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 4'); clf +spm_MDP_VB_LFP(MDP(1:8)); + +% illustrate familiarity (c.f., MMN) and context learning +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 5'); clf +spm_MDP_VB_LFP(MDP([2,16]),[1;2],2); +subplot(4,1,1), title('Repetition suppression and DA transfer','FontSize',16) + +spm_figure('GetWin','Figure 6'); clf +n = size(MDP(1).xn{1},1); +v = spm_MDP_VB_LFP(MDP([2,n]),[1;2],2); +t = ((1:n)*16 + 80)*16/n; +subplot(2,1,1),plot(t,v{1}{2,1},'b-.',t,v{2}{2,1},'b:',t,v{2}{2,1} - v{1}{2,1}) +xlabel('Time (ms)'),ylabel('LFP'),title('Difference waveform (MMN)','FontSize',16) +legend({'oddball','standard','MMN'}), grid on, axis square + + diff --git a/toolbox/DEM/DEM_demo_MDP_fit.m b/toolbox/DEM/DEM_demo_MDP_fit.m new file mode 100644 index 00000000..aefbe4b0 --- /dev/null +++ b/toolbox/DEM/DEM_demo_MDP_fit.m @@ -0,0 +1,263 @@ +function BMA = DEM_demo_MDP_fit +% Demo of active inference for trust games +%__________________________________________________________________________ +% +% This routine uses a Markov decision process formulation of active +% inference (with variational Bayes) to model foraging for information in a +% three arm maze. This demo illustrates the inversion of a single subject +% and group data to make inferences about subject specific parameters – +% such as their prior beliefs about precision and utility. +% +% We first generate some synthetic data for a single subject and illustrate +% the recovery of key parameters using variational Laplace. We then +% consider the inversion of multiple trials from a group of subjects to +% illustrate the use of empirical Bayes in making inferences at the between +% subject level – and the use of Bayesian cross-validation to retrieve out +% of sample estimates (and classification of new subjects) +% +% In this example, the agent starts at the centre of a three way maze +% which is baited with a reward in one of the two upper arms. However, the +% rewarded arm changes from trial to trial. Crucially, the agent can +% identify where the reward (US) is located by accessing a cue (CS) in the +% lower arm. This tells the agent whether the reward is on the left or the +% right upper arm. This means the optimal policy would first involve +% maximising information gain or epistemic value by moving to the lower arm +% and then claiming the reward this signified. Here, there are eight hidden +% states (four locations times right or left reward), four control states +% (that take the agent to the four locations) and seven outcomes (three +% locations times two cues plus the centre). The central location has an +% ambiguous or uninformative cue outcome, while the upper arms are rewarded +% probabilistically. +% +% see also: spm_MPD_VB.m, spm_dcm_mdp.m and spm_nlsi_Newton.m +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: DEM_demo_MDP_fit.m 6655 2015-12-23 20:21:27Z karl $ + +% set up and preliminaries: first generate synthetic (single subject) data +%========================================================================== +rng('default') + +% outcome probabilities: A +%-------------------------------------------------------------------------- +% We start by specifying the probabilistic mapping from hidden states +% to outcomes. +%-------------------------------------------------------------------------- +a = .95; +b = 1 - a; +A = [1 1 0 0 0 0 0 0; % ambiguous starting position (centre) + 0 0 a b 0 0 0 0; % left arm selected and rewarded + 0 0 b a 0 0 0 0; % left arm selected and not rewarded + 0 0 0 0 b a 0 0; % right arm selected and not rewarded + 0 0 0 0 a b 0 0; % right arm selected and rewarded + 0 0 0 0 0 0 1 0; % informative cue - reward on right + 0 0 0 0 0 0 0 1]; % informative cue - reward on left + +% controlled transitions: B{u} +%-------------------------------------------------------------------------- +% Next, we have to specify the probabilistic transitions of hidden states +% under each action or control state. Here, there are four actions taking the +% agent directly to each of the four locations. +%-------------------------------------------------------------------------- +B{1} = [1 0 0 1; 0 1 0 0;0 0 1 0;0 0 0 0]; +B{2} = [0 0 0 0; 1 1 0 1;0 0 1 0;0 0 0 0]; +B{3} = [0 0 0 0; 0 1 0 0;1 0 1 1;0 0 0 0]; +B{4} = [0 0 0 0; 0 1 0 0;0 0 1 0;1 0 0 1]; +for i = 1:4 + B{i} = kron(B{i},eye(2)); +end + +% priors: (utility) C +%-------------------------------------------------------------------------- +% Finally, we have to specify the prior preferences in terms of log +% probabilities. Here, the agent prefers rewarding outcomes +%-------------------------------------------------------------------------- +c = 2; +C = [0 c -c c -c 0 0]'; + +% now specify prior beliefs about initial state, in terms of counts +%-------------------------------------------------------------------------- +d = kron([1 0 0 0],[1 1])'; + +% allowable policies (of depth T). These are just sequences of actions +%-------------------------------------------------------------------------- +V = [1 1 1 1 2 3 4 4 4 4 + 1 2 3 4 2 3 1 2 3 4]; + + +% MDP Structure - this will be used to generate arrays for multiple trials +%========================================================================== +mdp.V = V; % allowable policies +mdp.A = A; % observation model +mdp.B = B; % transition probabilities +mdp.C = C; % preferred states +mdp.D = d; % prior over initial states +mdp.s = 1; % initial state + +mdp.alpha = 2; % precision of action selection +mdp.beta = 1; % inverse precision of policy selection + +% true parameters +%-------------------------------------------------------------------------- +n = 128; % number of trials +i = rand(1,n) > 1/2; % randomise hidden states over trials +P.beta = log(2); +P.C = log(2); + +MDP = mdp; +MDP.C = mdp.C*exp(P.C); +MDP.beta = mdp.beta*exp(P.beta); + + +[MDP(1:n)] = deal(MDP); +[MDP(i).s] = deal(2); + + +% Solve to generate data +%========================================================================== +MDP = spm_MDP_VB(MDP); + +% illustrate behavioural responses – single trial +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 1a'); clf +spm_MDP_VB_trial(MDP(1)); + +% illustrate behavioural responses and neuronal correlates over trials +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 1b'); clf +spm_MDP_VB_game(MDP); + +%-------------------------------------------------------------------------- +% This completes the generation of data. We now turn to the estimation of +% subject specific preferences and precision encoded by the parameters +% beta and C. Model parameters here are log scaling parameters that allow +% for increases or decreases in the default prior values. +%-------------------------------------------------------------------------- + + +% Invert to recover parameters (preferences and precision) +%========================================================================== +DCM.MDP = mdp; % MDP model +DCM.field = {'beta','C'}; % parameter (field) names to optimise +DCM.U = {MDP.o}; % trial specification (stimuli) +DCM.Y = {MDP.u}; % responses (action) + +DCM = spm_dcm_mdp(DCM); + +% compare true values with posterior estimates +%-------------------------------------------------------------------------- +subplot(2,2,4),hold on +bar(spm_vec(P),1/4) +set(gca,'XTickLabel',DCM.field) +set(gcf,'Name','Figure 2','Tag','Figure 2') + + +% now repeat using subsets of trials to illustrate effects on estimators +%========================================================================== +DCM.field = {'beta'}; +n = [8 16 32 64 128]; +for i = 1:length(n) + DCM.U = {MDP(1:n(i)).o}; + DCM.Y = {MDP(1:n(i)).u}; + DCM = spm_dcm_mdp(DCM); + Ep(i,1) = DCM.Ep.beta; + Cp(i,1) = DCM.Cp; +end + +% plus results +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 3'); clf +subplot(2,1,1), spm_plot_ci(Ep(:),Cp(:)), hold on +plot(1:length(n),(n - n) + P.beta,'k'), hold off +set(gca,'XTickLabel',n) +xlabel('number of trials','FontSize',12) +ylabel('conditional estimate','FontSize',12) +title('Dependency on trial number','FontSize',16) +axis square + + +% now repeat but over multiple subjects with different betsa +%========================================================================== + +% generate data and a between subject model with two groups of eight +% subjects +%-------------------------------------------------------------------------- +N = 8; % numbers of subjects per group +X = kron([1 1;1 -1],ones(N,1)); % design matrix +h = 4; % between subject log precision +n = 128; % number of trials +i = rand(1,n) > 1/2; % randomise hidden states + +clear MDP +[MDP(1:n)] = deal(mdp); +[MDP(i).s] = deal(2); + +for i = 1:size(X,1) + + % true parameters - with a group difference of one quarter + %---------------------------------------------------------------------- + beta(i) = X(i,:)*[0; 1/4] + exp(-h/2)*randn; + [MDP.beta] = deal(exp(beta(i))); + + % solve to generate data + %---------------------------------------------------------------------- + DDP = spm_MDP_VB(MDP); % realisation for this subject + DCM.U = {DDP.o}; % trial specification (stimuli) + DCM.Y = {DDP.u}; % responses (action) + GCM{i,1} = DCM; + + % plot behavioural responses + %---------------------------------------------------------------------- + spm_figure('GetWin','Figure 4'); clf + spm_MDP_VB_game(DDP);drawnow + +end + + +% Bayesian model inversion +%========================================================================== +GCM = spm_dcm_fit(GCM); + +% plot subject specific estimates and true values +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 4'); +subplot(3,1,3) +for i = 1:length(GCM) + qP(i) = GCM{i}.Ep.beta; +end +plot(beta,beta,':b',beta,qP,'.b','MarkerSize',32) +xlabel('true parameter','FontSize',12) +ylabel('conditional estimate','FontSize',12) +title('Subject specific estimates','FontSize',16) +axis square + + +% hierarchical (empirical) Bayes +%========================================================================== + +% second level model +%-------------------------------------------------------------------------- +M = struct('X',X); + +% BMA - (second level) +%-------------------------------------------------------------------------- +PEB = spm_dcm_peb(GCM,M); +BMA = spm_dcm_peb_bmc(PEB); + +subplot(3,2,4),hold on, bar(1,1/4,1/4), set(gca,'XTickLabel',DCM.field) +subplot(3,2,2),hold on, bar(1,1/4,1/4), set(gca,'XTickLabel',DCM.field) + + +% posterior predictive density and cross validation +%========================================================================== +spm_dcm_loo(GCM,M,DCM.field); + + + + + + + + diff --git a/toolbox/DEM/DEM_demo_MDP_habits.m b/toolbox/DEM/DEM_demo_MDP_habits.m index e180ddef..d58731a2 100644 --- a/toolbox/DEM/DEM_demo_MDP_habits.m +++ b/toolbox/DEM/DEM_demo_MDP_habits.m @@ -2,7 +2,7 @@ % Demo of active inference for trust games %__________________________________________________________________________ % -% This routine uses the Markov decision process formulation of active +% This routine uses a Markov decision process formulation of active % inference (with variational Bayes) to model foraging for information in a % three arm maze. This demo illustrates variational free energy % minimisation in the context of Markov decision processes, where the agent @@ -21,38 +21,49 @@ % maximising information gain or epistemic value by moving to the lower arm % and then claiming the reward this signified. Here, there are eight hidden % states (four locations times right or left reward), four control states -% (that take the agent to the four locations) and 16 outcomes (four -% locations times two cues times two rewards). The central location has an +% (that take the agent to the four locations) and five outcomes (two +% locations times two cues plus the centre). The central location has an % ambiguous or uninformative cue outcome, while the upper arms are rewarded -% probabilistically with an 80% schedule. +% probabilistically. % -% A single trial is simulated followed by an examination of dopaminergic -% responses to conditioned and unconditioned stimuli (cues and rewards). A -% hierarchical version is then implemented, in which the mapping between -% locations in the generative model and the generative process is unknown -% and has to be learned. +% This version focuses on learning by optimising the parameters of the +% generative model. In particular, it looks at the acquisition of epistemic +% habits – and how they relate to optimal policies under dynamic +% programming. We start with a series of simulations to illustrate various +% phenomena in electrophysiology and then move on to learning per se. % % see also: spm_MPD_game %__________________________________________________________________________ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: DEM_demo_MDP_habits.m 6450 2015-05-24 14:28:03Z karl $ - +% $Id: DEM_demo_MDP_habits.m 6655 2015-12-23 20:21:27Z karl $ + % set up and preliminaries %========================================================================== -% rng('default') - -% observation probabilities +rng('default') + +% outcome probabilities: A %-------------------------------------------------------------------------- -a = .9; -A{1,1} = [.5 .5; .5 .5]; -A{2,2} = [a (1 - a); (1 - a) a]; -A{3,3} = [(1 - a) a; a (1 - a)]; -A{4,4} = [1 0; 0 1]; -A = spm_cat(A); - -% transition probabilities: states = [centre,L,R,cue] x [R,L] +% We start by specifying the probabilistic mapping from hidden states +% to outcomes. This necessarily requires one to think carefully about the +% hidden states [centre, right, left, cue] x [reward, loss] and the ensuing +% outcomes +%-------------------------------------------------------------------------- +a = .98; +b = 1 - a; +A = [1 1 0 0 0 0 0 0; % ambiguous starting position (centre) + 0 0 a b b a 0 0; % reward + 0 0 b a a b 0 0; % no reward + 0 0 0 0 0 0 1 0; % informative cue - reward on right + 0 0 0 0 0 0 0 1]; % informative cue - reward on left + +% controlled transitions: B{u} +%-------------------------------------------------------------------------- +% Next, we have to specify the probabilistic transitions of hidden states +% under each action or control state. Here, there are four actions taking the +% agent directly to each of the four locations. Some of these locations are +% absorbing states; in that once entered, they cannot be left %-------------------------------------------------------------------------- for i = 1:4 B{i} = zeros(4,4); @@ -60,235 +71,263 @@ B{i}(i,[1 4]) = 1; B{i} = kron(B{i},eye(2)); end - -% priors: softmax(utility) + +% priors: (utility) C %-------------------------------------------------------------------------- -c = 2; -C = spm_softmax(c*[-1 -1 1 -1 -1 1 0 0]'); - -% prior beliefs about initial state +% Finally, we have to specify the prior preferences in terms of log +% probabilities. Here, the agent prefers rewards to losses. %-------------------------------------------------------------------------- -D = kron([1 0 0 0],[1 1]/2)'; - -% true initial state +c = 3; +C = [0 0 0 0 0; + 0 c -c 0 0; + 0 c -c 0 0]'; + +% now specify prior beliefs about initial state, in terms of counts %-------------------------------------------------------------------------- -S = kron([1 0 0 0],[1 0])'; - - -% allowable policies (of depth T) +d = kron([1 0 0 0],[8 8])'; + +% allowable policies (of depth T). These are just sequences of actions %-------------------------------------------------------------------------- V = [1 1 1 1 2 3 4 4 4 4 1 2 3 4 2 3 1 2 3 4]; - -% MDP Structure +% prior beliefs about policies +%-------------------------------------------------------------------------- +Np = size(V,2); +e = [4 + zeros(Np,1); 1]; + + +% MDP Structure - this will be used to generate arrays for multiple trials %========================================================================== -MDP.N = 8; % number of variational iterations -MDP.S = S; % true initial state -MDP.A = A; % observation model -MDP.B = B; % transition probabilities (priors) -MDP.C = C; % terminal cost probabilities (priors) -MDP.D = D; % initial state probabilities (priors) -MDP.V = V; % allowable policies - -MDP.alpha = 64; % gamma hyperparameter -MDP.beta = 4; % gamma hyperparameter -MDP.lambda = 1/4; % precision update rate +mdp.V = V; % allowable policies +mdp.A = A; % observation model +mdp.B = B; % transition probabilities +mdp.C = C; % preferred states +mdp.d = d; % prior over initial states +mdp.s = 1; % true initial state + +% true initial states – with context change at trial 12 +%-------------------------------------------------------------------------- +i = [1,3]; % change context in a couple of trials +[MDP(1:32)] = deal(mdp); % create structure array +[MDP(i).s] = deal(2); % deal context changes +% [MDP.C] = deal(C - C); % for epistemic simulation +MDP(12).o = [1 4 5]; % unexpected outcome -% Solve - an example game + +% Solve - an example game: a run of reds then an oddball %========================================================================== -spm_figure('GetWin','Figure 1'); clf -MDP.plot = gcf; -MDP = spm_MDP_VB(MDP,'FE'); - - -return +MDP = spm_MDP_VB(MDP); + +% illustrate behavioural responses – single trial +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 1a'); clf +spm_MDP_VB_trial(MDP(1)); +% illustrate behavioural responses and neuronal correlates +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 1b'); clf +spm_MDP_VB_game(MDP); -% different formulations of optimality as a function of preference -%========================================================================== +% illustrate phase-precession and responses to chosen option - 1st trial +%-------------------------------------------------------------------------- spm_figure('GetWin','Figure 2'); clf -MDP.plot = 0; -MDP.N = 4; - -n = 128; % number of simulated trials -c = linspace(0,1,6); -d = (A*[0 0 1 0 0 1 0 0]') > 1/2; -for i = 1:length(c) - - % preference - %---------------------------------------------------------------------- - MDP.C = spm_softmax(c(i)*[-1 -1 1 -1 -1 1 0 0]'); - - - % simulate trials and record outcomes - %---------------------------------------------------------------------- - for j = 1:n - - % randomise reward - %------------------------------------------------------------------ - s = rand > 1/2; - MDP.S = kron([1 0 0 0],[s ~s])'; - - - % invert under different schemes - %------------------------------------------------------------------ - MDP = spm_MDP_VB(MDP,'FE'); - FE(j,i) = d'*MDP.O(:,end); - MDP = spm_MDP_VB(MDP,'KL'); - KL(j,i) = d'*MDP.O(:,end); - MDP = spm_MDP_VB(MDP,'RL'); - RL(j,i) = d'*MDP.O(:,end); - MDP = spm_MDP_VB(MDP,'FE',1); - FP(j,i) = d'*MDP.O(:,end); - try MDP = rmfield(MDP,'w'); end - - end - -end -MDP.S = S; -MDP.C = C; +spm_MDP_VB_LFP(MDP(1),[4 6;3 3]); -% plot behavioural results +% place cells %-------------------------------------------------------------------------- -subplot(3,1,1) -bar(c,[mean(FE); mean(KL); mean(RL); mean(FP)]'*100), hold on -plot(c,c*0 + 100*3/8,'--k'), hold on -plot(c,c*0 + 100*a, '-.k'), hold off -title('Performance','FontSize',16) -xlabel('Prior preference','FontSize',12) -ylabel('success rate (%)','FontSize',12) -spm_axis tight, axis square -legend({'FE','KL','RL','DA'}) - +spm_figure('GetWin','Figure 3'); clf +subplot(2,2,1),spm_MDP_VB_place_cell(MDP(1:6),[3 6;3 3]); +subplot(2,2,2),spm_MDP_VB_place_cell(MDP(1:6),[7 8;2 2]); -% dopamine responses to US and CS -%========================================================================== -OPT = 'FE'; -MDP.N = 16; -MDP.a = [4 2]; -MDP.o = [1 7 3]; -MDP = spm_MDP_VB(MDP,OPT); - -% axis +% illustrate phase-amplitude (theta-gamma) coupling %-------------------------------------------------------------------------- -pst = (1:length(MDP.d))*100/1000; -ax = [1 pst(end) 0 4]; - -subplot(3,2,3) -plot(pst,MDP.d,'k'), hold on -subplot(3,2,5) -plot(pst,MDP.da,'k'), hold on +spm_figure('GetWin','Figure 4'); clf +spm_MDP_VB_LFP(MDP(1:8)); - -subplot(3,2,6) -r = 128; -bar(pst,r*MDP.da + randn(size(MDP.da)).*sqrt(r*MDP.da)) -title('Simulated (CS & US) responses','FontSize',16) -xlabel('Peristimulus time (sec)','FontSize',12) -ylabel('Rate','FontSize',12) -axis square, set(gca,'XLim',ax(1:2)) - - -% repeat but with the US only +% illustrate oddball responses (P300) - US %-------------------------------------------------------------------------- -MDP.a = [1 2]; -MDP.o = [1 1 3]; -MDP = spm_MDP_VB(MDP,OPT); - -subplot(3,2,3) -plot(pst,MDP.d,'r'), hold off -title('Precision updates','FontSize',16) -xlabel('Peristimulus time (sec)','FontSize',12) -ylabel('Precision','FontSize',12) -axis square, set(gca,'XLim',ax(1:2)) +spm_figure('GetWin','Figure 5'); clf +spm_MDP_VB_LFP(MDP([11,12]),[8;3]); +subplot(4,1,1), title('Violation response (P300)','FontSize',16) + +% illustrate oddball responses (MMN) - CS +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 6a'); clf +spm_MDP_VB_LFP(MDP([2,20]),[1;1]); +subplot(4,1,1), title('Repetition suppression and DA transfer','FontSize',16) + +spm_figure('GetWin','Figure 6b');clf +v = spm_MDP_VB_LFP(MDP([2,20]),[1 2;1 1]); +t = (1:16)*16 + 80; +subplot(2,1,1),plot(t,v{1}{2,1},'b-.',t,v{2}{2,1},'b:',t,v{2}{2,1} - v{1}{2,1}) +xlabel('Time (ms)'),ylabel('LFP'),title('Difference waveform (MMN)','FontSize',16) +legend({'oddball','standard','MMN'}), grid on, axis square -subplot(3,2,5) -plot(pst,MDP.da,'r'), hold off -title('Dopamine responses','FontSize',16) -xlabel('Peristimulus time (sec)','FontSize',12) -ylabel('Response','FontSize',12) -axis square, axis(ax) +% return -subplot(3,2,4) -r = 128; -bar(pst,r*MDP.da + randn(size(MDP.da)).*sqrt(r*MDP.da)) -title('Simulated (US) responses','FontSize',16) -xlabel('Peristimulus time (sec)','FontSize',12) -ylabel('Rate','FontSize',12) -axis square, set(gca,'XLim',ax(1:2)) +% illustrate reversal learning - after trial 32 +%========================================================================== +clear MDP +[MDP(1:64)] = deal(mdp); +[MDP(32:64).s] = deal(2); + +% just learn the context +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 7'); clf +spm_MDP_VB_game(spm_MDP_VB(MDP)); + +% the effect of prior exposure on reversal learning +%-------------------------------------------------------------------------- +clear MDP +[MDP(1:16)] = deal(mdp); +[MDP(4:16).s] = deal(2); +OPTIONS.plot = 0; + +d = 2:2:8; +for i = 1:length(d) + MDP(1).d = kron([1 0 0 0],[d(i) 1])' + 1; + M = spm_MDP_VB(MDP,OPTIONS); + Q = spm_MDP_VB_game(M); + ext(i) = sum(Q.O(4:end) == 3); +end + +spm_figure('GetWin','Figure 8'); clf +subplot(2,1,1); bar(d,ext,'c'), axis square +xlabel('Previous exposures'), ylabel('Trials until reversal') +title('Reversal learning','FontSize',16) -% different levels of priors and uncertainty + +% illustrate devaluation: enable habit learning from now on %========================================================================== -spm_figure('GetWin','Figure 3'); clf; +OPTIONS.habit = 1; +mdp.e = e; +N = 64; -MDP.a = [4 2]; -MDP.o = [1 7 3]; -n = 3; -c = linspace(0,2,n); -for i = 1:length(c) - - % preference - %---------------------------------------------------------------------- - MDP.C = spm_softmax(c(i)*[-1 -1 1 -1 -1 1 0 0]'); - - % simulate trials and record outcomes - %---------------------------------------------------------------------- - MDP = spm_MDP_VB(MDP,'FE'); - col = [1 1 1]*(length(c) - i)/length(c); - - subplot(2,2,1) - plot(pst,MDP.da,'Color',col), hold on - title('Preference (utility)','FontSize',16) - xlabel('Peristimulus time (sec)','FontSize',12) - ylabel('Response','FontSize',12) - axis square, axis(ax) - - subplot(2*n,2,(i - 1)*2 + 2) - r = 128; - bar(pst,r*MDP.da + randn(size(MDP.da)).*sqrt(r*MDP.da)) - ylabel('Rate','FontSize',12) - axis square, set(gca,'XLim',ax(1:2)) - -end +% devalue (i.e. switch) preferences after habitisation (trial 32) +%-------------------------------------------------------------------------- +clear MDP; + +[MDP(1:N)] = deal( mdp); +[MDP(48:end).C] = deal(-mdp.C); + +spm_figure('GetWin','Figure 9'); clf +spm_MDP_VB_game(spm_MDP_VB(MDP,OPTIONS)); + +% repeat but now devalue before habit formation (at trial 8) +%-------------------------------------------------------------------------- +[MDP(16:end).C] = deal(-mdp.C); + +spm_figure('GetWin','Figure 10'); clf +spm_MDP_VB_game(spm_MDP_VB(MDP,OPTIONS)); + + + +% illustrate epistemic habit leaning +%========================================================================== + +% true initial states +%-------------------------------------------------------------------------- +clear MDP; +i = rand(1,N) > 1/2; +[MDP(1:N)] = deal(mdp); +[MDP(i).s] = deal(2); -% and uncertainty + +% habitual (non-sequential) policy %-------------------------------------------------------------------------- -subplot(2,1,2) +spm_figure('GetWin','Figure 11'); clf +M = spm_MDP_VB(MDP,OPTIONS); spm_MDP_VB_game(M); +h = M(end).c; +h = h*diag(1./sum(h)); + +subplot(3,3,7); image(64*(1 - h)), axis square +xlabel('Hidden state'), xlabel('Hidden state') +title('Epistemic habit','FontSize',16) + +% get equivalent dynamic programming solutions +%-------------------------------------------------------------------------- +[B0,BV] = spm_MDP_DP(MDP(1)); + +subplot(3,3,8); image(64*(1 - spm_softmax(B0))), axis square +xlabel('Hidden state'), xlabel('Hidden state') +title('Active inference','FontSize',16) + +subplot(3,3,9); image(64*(1 - BV)), axis square +xlabel('Hidden state'), xlabel('Hidden state') +title('Dynamic programming','FontSize',16) + + +% now repeat with unambiguous outcomes +%-------------------------------------------------------------------------- +A = [1 0 0 0 0 0 0 0; + 0 1 0 0 0 0 0 0 + 0 0 a b 0 0 0 0; + 0 0 b a 0 0 0 0; + 0 0 0 0 b a 0 0; + 0 0 0 0 a b 0 0; + 0 0 0 0 0 0 1 0; + 0 0 0 0 0 0 0 1]; + +C = [0 0 0 0 0 0 0 0; + 0 0 c -c c -c 0 0; + 0 0 c -c c -c 0 0]'; + +[MDP.A] = deal(A); +[MDP.C] = deal(C); + +% habitual (non-sequential) policy +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 12'); clf +M = spm_MDP_VB(MDP,OPTIONS); spm_MDP_VB_game(M); +h = M(end).c; +h = h*diag(1./sum(h)); + +subplot(3,3,7); image(64*(1 - h)), axis square +xlabel('Hidden state'), xlabel('Hidden state') +title('Epistemic habit','FontSize',16) + +% get equivalent dynamic programming solutions +%-------------------------------------------------------------------------- +[B0,BV] = spm_MDP_DP(MDP(1)); + +subplot(3,3,8); image(64*(1 - spm_softmax(B0))), axis square +xlabel('Hidden state'), xlabel('Hidden state') +title('Active inference','FontSize',16) + +subplot(3,3,9); image(64*(1 - BV)), axis square +xlabel('Hidden state'), xlabel('Hidden state') +title('Dynamic programming','FontSize',16) + +return -MDP.C = C; -c = linspace(.5,.9,n); -for i = 1:length(c) - - % preference - %---------------------------------------------------------------------- - a = c(i); - U{1,1} = [.5 .5; .5 .5; 0 0; 0 0]; - U{2,2} = [0 0; 0 0; a (1 - a); (1 - a) a]; - U{3,3} = [0 0; 0 0; (1 - a) a; a (1 - a)]; - U{4,4} = [1 0; 0 1; 0 0; 0 0]; - MDP.A = spm_cat(U); - - % simulate trials and record outcomes - %---------------------------------------------------------------------- - MDP = spm_MDP_VB(MDP,'FE'); - col = [1 1 1]*(length(c) - i)/length(c); - - subplot(2,2,3) - plot(pst,MDP.da,'Color',col), hold on - title('Uncertainty','FontSize',16) - xlabel('Peristimulus time (sec)','FontSize',12) - ylabel('Response','FontSize',12) - axis square, axis(ax) - - subplot(2*n,2,(i - 1)*2 + 2 + n*2) - r = 128; - bar(pst,r*MDP.da + randn(size(MDP.da)).*sqrt(r*MDP.da)) - ylabel('Rate','FontSize',12) - axis square, set(gca,'XLim',ax(1:2)) - -end -xlabel('Peristimulus time','FontSize',12) +function spm_MDP_VB_place_cell(MDP,UNITS) +% place cell plotting subroutine +%-------------------------------------------------------------------------- +col = {'r','g','b','m','c'}; +for t = 1:length(MDP) + for j = 1:size(UNITS,2) + + [~,v] = spm_MDP_VB_LFP(MDP(t),UNITS(:,j)); + qu = spm_cat(v); + qe = randn(2,size(qu,1))/4; + L = [0 0 -1 -1 1 1 0 0; + 0 0 1 1 1 1 -1 -1]; + for i = 1:size(qu,1) + X(:,i) = L(:,MDP(t).s(ceil(i/16))) + qe(:,i); + end + X = spm_conv(X,0,3); + plot(X(1,:),X(2,:),'r:'), hold on + for i = 1:size(qu,1) + if qu(i) > .80 + plot(X(1,i),X(2,i),'.','MarkerSize',16,'Color',col{j}) + end + end + + end +end +axis([-2 2 -2 2]), axis square, hold off +title('Place field responses','fontsize',16) diff --git a/toolbox/DEM/DEM_demo_MDP_reading.m b/toolbox/DEM/DEM_demo_MDP_reading.m new file mode 100644 index 00000000..f406e1d2 --- /dev/null +++ b/toolbox/DEM/DEM_demo_MDP_reading.m @@ -0,0 +1,618 @@ +function MDP = DEM_demo_MDP_reading +% Demo of active inference for visual salience +%__________________________________________________________________________ +% +% This routine uses active inference for Markov decision processes to +% illustrate epistemic foraging in the context of visual searches. Here, +% the agent has to categorise scenes on the basis of the relative position +% of various cues. Crucially, the agent can only sample one cue or location +% at a time and therefore has to accumulate evidence for competing +% hypotheses. This rests upon resolving uncertainty about which scene or +% hypothesis is in play through the minimisation of expected free energy. +% +% When the agent become sufficiently confident about the underlying scene, +% it then makes a saccade to a choice location - to obtain feedback (right +% or wrong). The agent prefers to be right and does not expect to be +% wrong. We first illustrate a single trial in terms of behaviour and +% electrophysiological responses. We then consider sequences of trials and +% how average behaviour (accuracy, number of saccades and saccade duration) +% depends upon prior preferences and prior precision. +% +% This demonstration uses a factorised version of the MDP scheme. In +% other words, we assume a mean field approximation to the posterior over +% different hidden states (context, location, scene reflection) – and over +% multiple modalities (what versus where). This provides a parsimonious +% representation of posterior beliefs over hidden states – but does induce +% degree of overconfidence associated with approximate Bayesian inference. +% +% see also: DEM_demo_MDP_habits.m and spm_MPD_VB_X.m +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: DEM_demo_MDP_reading.m 6672 2016-01-12 12:28:31Z karl $ + +% set up and preliminaries +%========================================================================== +% In this example, an agent has to categorise a scene that comprises +% potential cues at four peripheral locations, starting from a central +% fixation point. This involves a form of scene construction, in which the +% relationship between various cues determines the category of scene. In +% brief, the scene always contains a bird and seed, or bird and a cat. If +% the bird is next to the seed or the cat, then the scene is categorised as +% feed or flee respectively. Conversely, if the seed is in an opposite +% diagonal location, the category is wait. The particular positions of the +% cues are irrelevant, the important attribute are there relationships. +% This means hidden states have to include some spatial mappings that +% induce invariances to spatial transformations. These are reflections +% around the horizontal and vertical axes. +% +% There are two outcome modalities (what and where), encoding one of six +% cues and one of eight locations (there are three extra locations that +% provide feedback about the respective decision). The hidden states +% have four factors; corresponding to context (the three categories), where +% (the eight locations) and two further factors modelling invariance +%-------------------------------------------------------------------------- +% rng('default') + + +% first level (lexcial) +%========================================================================== + +% prior beliefs about initial states (in terms of counts_: D and d +%-------------------------------------------------------------------------- +D{1} = [1 1 1]'; % what: {'flee','feed','wait'} +D{2} = [1 0 0 0]'; % where: {'1',...,'4'} +D{3} = [1 1]'; % flip(ud): {'no','yes'} +D{4} = [1 1]'; % flip(rl): {'no','yes'} + + +% probabilistic mapping from hidden states to outcomes: A +%-------------------------------------------------------------------------- +Nf = numel(D); +for f = 1:Nf + Ns(f) = numel(D{f}); +end +for f1 = 1:Ns(1) + for f2 = 1:Ns(2) + for f3 = 1:Ns(3) + for f4 = 1:Ns(4) + + % location of cues for this hidden state + %---------------------------------------------------------- + if f1 == 1, a = {'bird','cat' ;'null','null'}; end + if f1 == 2, a = {'bird','seed';'null','null'}; end + if f1 == 3, a = {'bird','null';'null','seed'}; end + + % flip cues according to hidden (invariants) states + %---------------------------------------------------------- + if f3 == 2, a = flipud(a); end + if f4 == 2, a = fliplr(a); end + + % A{1} what: {'null','bird,'seed','cat'} + %========================================================== + + % saccade to cue location + %---------------------------------------------------------- + A{1}(1,f1,f2,f3,f4) = strcmp(a{f2},'null'); + A{1}(2,f1,f2,f3,f4) = strcmp(a{f2},'bird'); + A{1}(3,f1,f2,f3,f4) = strcmp(a{f2},'seed'); + A{1}(4,f1,f2,f3,f4) = strcmp(a{f2},'cat'); + + % A{2} where: {'start','1',...,'4','flee','feed','wait'} + %---------------------------------------------------------- + A{2}(f2,f1,f2,f3,f4) = 1; + + end + end + end +end + +% controlled transitions: B{f} for each factor +%-------------------------------------------------------------------------- +for f = 1:Nf + B{f} = eye(Ns(f)); +end + +% controllable fixation points: move to the k-th location +%-------------------------------------------------------------------------- +for k = 1:Ns(2) + B{2}(:,:,k) = 0; + B{2}(k,:,k) = 1; +end + + +% MDP Structure +%-------------------------------------------------------------------------- +mdp.T = 6; % number of moves +mdp.A = A; % observation model +mdp.B = B; % transition probabilities +mdp.D = D; % prior over initial states + +mdp.Aname = {'what','where'}; +mdp.Bname = {'what','where','flip','flip'}; + +clear A B D + +MDP = spm_MDP_check(mdp); +clear mdp + +% second level (semantic) +%========================================================================== + +% prior beliefs about initial states (in terms of counts_: D and d +%-------------------------------------------------------------------------- +D{1} = [1 1 1 1 1 1]'; % what: {'story 1',...,'story 6'} +D{2} = [1 0 0 0]'; % where: {'1',...,'4'} +D{3} = [1 0 0]'; % report: {'null','happy','sad'} + +% probabilistic mapping from hidden states to outcomes: A +%-------------------------------------------------------------------------- +Nf = numel(D); +for f = 1:Nf + Ns(f) = numel(D{f}); +end +for f1 = 1:Ns(1) + for f2 = 1:Ns(2) + for f3 = 1:Ns(3) + + % sequence of pictures for each story + %-------------------------------------------------------------- + if f1 == 1, a = {'flee','wait','feed','wait'}; end % happy + if f1 == 2, a = {'wait','wait','feed','wait'}; end % happy + if f1 == 3, a = {'feed','flee','wait','feed'}; end % happy + if f1 == 4, a = {'flee','wait','flee','wait'}; end % sad + if f1 == 5, a = {'wait','feed','wait','flee'}; end % sad + if f1 == 6, a = {'wait','flee','feed','flee'}; end % sad + + + % A{1} picture: 'flee','feed','wait' + %============================================================== + A{1}(1,f1,f2,f3) = strcmp(a{f2},'flee'); + A{1}(2,f1,f2,f3) = strcmp(a{f2},'feed'); + A{1}(3,f1,f2,f3) = strcmp(a{f2},'wait'); + + % A{2} where: {'1',...,'3'} + %-------------------------------------------------------------- + A{2}(f2,f1,f2,f3) = 1; + + % A{3} feedback: {'null','right','wrong'} + %-------------------------------------------------------------- + hap = any(ismember([1 2 3],f1)); + sad = any(ismember([4 5 6],f1)); + A{3}(1,f1,f2,f3) = (f3 == 1); + A{3}(2,f1,f2,f3) = (f3 == 2 & hap) | (f3 == 3 & sad); + A{3}(3,f1,f2,f3) = (f3 > 1 & ~A{3}(2,f1,f2,f3)); + + end + end +end +Ng = numel(A); +for g = 1:Ng + No(g) = size(A{g},1); +end + +% controlled transitions: B{f} for each factor +%-------------------------------------------------------------------------- +for f = 1:Nf + B{f} = eye(Ns(f)); +end + +% control states B(2): where {'stay','forward,'backward'} +%-------------------------------------------------------------------------- +B{2}(:,:,1) = spm_speye(Ns(2),Ns(2), 0); +B{2}(:,:,2) = spm_speye(Ns(2),Ns(2),-1); B{2}(end,end,2) = 1; +B{2}(:,:,3) = spm_speye(Ns(2),Ns(2), 1); B{2}(1,1,3) = 1; + +% control states B(3): report {'null,'happy','sad'} +%-------------------------------------------------------------------------- +for k = 1:Ns(3) + B{3}(:,:,k) = 0; + B{3}(k,:,k) = 1; +end + +% allowable policies (specified as the next action) U +%-------------------------------------------------------------------------- +U(1,1,:) = [1 1 1]'; % stay on this page +U(2,1,:) = [1 2 1]'; % and then move forward +U(1,2,:) = [1 2 1]'; % move to next page +U(2,2,:) = [1 2 1]'; % and then move forward + +U(1,3,:) = [1 1 2]'; % stay on current page and report happy +U(2,3,:) = [1 1 2]'; % stay on current page and report happy +U(1,4,:) = [1 1 3]'; % stay on current page and report sad +U(2,4,:) = [1 1 3]'; % stay on current page and report sad + + +% priors: (utility) C +%-------------------------------------------------------------------------- +for g = 1:Ng + C{g} = zeros(No(g),1); +end +C{3}(2,:) = 2; % the agent expects to be right +C{3}(3,:) = -6; % and not wrong + + +% MDP Structure +%-------------------------------------------------------------------------- +mdp.MDP = MDP; + +mdp.T = 5; % number of moves +mdp.U = U; % allowable policies +mdp.A = A; % observation model +mdp.B = B; % transition probabilities +mdp.C = C; % preferred outcomes +mdp.D = D; % prior over initial states +mdp.s = [1 1 1]'; % initial state + +mdp.Aname = {'picture','where','feedback'}; +mdp.Bname = {'story','where','decision'}; + +mdp = spm_MDP_check(mdp); + + +% illustrate a single trial +%========================================================================== +MDP = spm_MDP_VB_X(mdp); + +% show belief updates (and behaviour) +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 1'); clf +spm_MDP_VB_trial(MDP); + +return + +subplot(3,2,3) +spm_MDP_search_plot(MDP) + + +% illustrate phase-precession and responses +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 2'); clf +spm_MDP_VB_LFP(MDP,[],1); + + +% illustrate evidence accumulation and perceptual synthesis +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 3'); clf +spm_MDP_search_percept(MDP) + +return + +% illustrate a sequence of trials +%========================================================================== + +% true initial states – with context change at trial 12 +%-------------------------------------------------------------------------- +clear MDP +N = 32; +s(1,:) = ceil(rand(1,N)*3); +s(2,:) = ceil(rand(1,N)*1); +s(3,:) = ceil(rand(1,N)*2); +s(4,:) = ceil(rand(1,N)*2); + +for i = 1:N + MDP(i) = mdp; % create structure array + MDP(i).s = s(:,i); % context +end + + +% Solve - an example sequence +%========================================================================== +MDP = spm_MDP_VB_X(MDP); + +% illustrate behavioural responses and neuronal correlates +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 4'); clf +spm_MDP_VB_game(MDP); + +% illustrate phase-amplitude (theta-gamma) coupling +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 5'); clf +spm_MDP_VB_LFP(MDP(1:8)); + +% illustrate behaviour in more detail +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 6'); clf; +n = 3; +for i = 1:n*n + subplot(n,n,i), spm_MDP_search_plot(MDP(i)); +end + + +% illustrate the effects of epistemic and incentive salience +%========================================================================== +mdp.beta = 1; +mdp.T = 8; % maximum number of saccades + +c = (0:8)/8; +c = -4*c; +for j = 1:length(c) + + % array of trials + %---------------------------------------------------------------------- + clear MDP + for i = 1:N + MDP(i) = mdp; % create structure array + MDP(i).C{1}(6,:) = c(j); % salience + MDP(i).s = s(:,i); % context + end + + % solve and evaluate performance + %---------------------------------------------------------------------- + MDP = spm_MDP_VB_X(MDP); + for i = 1:N + o = MDP(i).o(1,:); % outcomes + P(1,i) = double(any(o == 5) & ~any(o == 6)); % accuracy + P(2,i) = find([(o > 4), 1],1) - 1; % number of saccades + P(3,i) = mean(MDP(i).rt); % reaction time + end + + % solve and evaluate performance + %---------------------------------------------------------------------- + EQ(:,j) = mean(P,2); +end + +spm_figure('GetWin','Figure 7'); clf; + +subplot(3,3,1), bar(-c,EQ(1,:)*100) +xlabel('Prior preference'),ylabel('percent correct') +title('Accuracy','Fontsize',16), axis square + +subplot(3,3,2), bar(-c,EQ(2,:)) +xlabel('Prior preference'),ylabel('number of saccades') +title('Decision time','Fontsize',16), axis square + +subplot(3,3,3), bar(-c,EQ(3,:)) +xlabel('Prior preference'),ylabel('saccade duration') +title('Reaction time','Fontsize',16), axis square + +% now repeat but changing prior precision +%-------------------------------------------------------------------------- +b = (0:8)/8; +b = 1./(2*b + 1/8); +for j = 1:length(b) + + % array of trials + %---------------------------------------------------------------------- + clear MDP + for i = 1:N + MDP(i) = mdp; % create structure array + MDP(i).beta = b(j); % prior (inverse) precision + MDP(i).s = s(:,i); % context + end + + % solve and evaluate performance + %---------------------------------------------------------------------- + MDP = spm_MDP_VB_X(MDP); + for i = 1:N + o = MDP(i).o(1,:); % outcomes + P(1,i) = double(any(o == 5) & ~any(o == 6)); % accuracy + P(2,i) = find([(o > 4), 1],1) - 1; % number of saccades + P(3,i) = mean(MDP(i).rt); % reaction time + end + + % solve and evaluate performance + %---------------------------------------------------------------------- + EP(:,j) = mean(P,2); +end + +subplot(3,3,4), bar(1./b,EP(1,:)*100) +xlabel('Prior precision'),ylabel('percent correct') +title('Accuracy','Fontsize',16), axis square + +subplot(3,3,5), bar(1./b,EP(2,:)) +xlabel('Prior precision'),ylabel('number of saccades') +title('Decision time','Fontsize',16), axis square + +subplot(3,3,6), bar(1./b,EP(3,:)) +xlabel('Prior precision'),ylabel('saccade duration') +title('Reaction time','Fontsize',16), axis square + +save + +return + + it + + +function spm_MDP_search_plot(MDP) +% illustrates visual search graphically +%-------------------------------------------------------------------------- + +% locations +%-------------------------------------------------------------------------- +x = [0,0;-1 -1; -1 1; 1 -1;1 1;-1,2.5;0,2.5;1,2.5]; +y = x + 1/4; +r = [-1,1]/2; + +% plot cues +%-------------------------------------------------------------------------- +if strcmp('replace',get(gca,'Nextplot')) + + % load images + %---------------------------------------------------------------------- + load MDP_search_graphics + null = zeros(size(bird)) + 1; + f = MDP.s(:,1); + + % latent cues for this hidden state + %---------------------------------------------------------------------- + if f(1) == 1, a = {'bird','cats';'null','null'}; end + if f(1) == 2, a = {'bird','seed';'null','null'}; end + if f(1) == 3, a = {'bird','null';'null','seed'}; end + + % flip cues according to hidden (invariants) states + %---------------------------------------------------------------------- + if f(3) == 2, a = flipud(a); end + if f(4) == 2, a = fliplr(a); end + + for i = 1:numel(a) + image(r + x(i + 1,1),r + x(i + 1,2),eval(a{i})), hold on + end + + % choices + %---------------------------------------------------------------------- + choice = {'flee','feed','wait'}; + for i = 1:3 + if i == f(1) + text(i - 2,2.5,choice{i},'FontWeight','Bold','color','red') + else + text(i - 2,2.5,choice{i}) + end + + end + axis image, axis([-2,2,-2,3]) + + % labels + %---------------------------------------------------------------------- + for i = 1:size(x,1) + text(y(i,1),y(i,2),num2str(i),'FontSize',12,'FontWeight','Bold','color','g') + end +end + +% Extract and plot eye movements +%-------------------------------------------------------------------------- +for i = 1:numel(MDP.o(2,:)) + X(i,:) = x(MDP.o(2,i),:); +end +for j = 1:2 + T(:,j) = interp(X(:,j),8,2); + T(:,j) = T(:,j) + spm_conv(randn(size(T(:,j))),2)/16; +end +plot(T(:,1),T(:,2),'b:') +plot(X(:,1),X(:,2),'b.','MarkerSize',8) + +function spm_MDP_search_percept(MDP) +% illustrates visual search graphically +%-------------------------------------------------------------------------- + +% load images +%-------------------------------------------------------------------------- +load MDP_search_graphics +clf + +null = zeros(size(bird)) + 1; +mask = hamming(256); +mask = mask*mask'; +for i = 1:3 + mask(:,:,i) = mask(:,:,1); +end +x = [0,0;-1 -1; -1 1; 1 -1;1 1;-1,2.5;0,2.5;1,2.5]; +r = [-1,1]/2; +try + d = MDP.D; +catch + d = MDP.d; +end +Nf = numel(d); +for f = 1:Nf + Ns(f) = numel(d{f}); +end + +% plot cues +%-------------------------------------------------------------------------- +Ni = 1:size(MDP.xn{1},1); +Nx = length(Ni); +Ne = find([MDP.o(1,:) > 4,1],1) - 1; +for k = 1:Ne + for i = 1:Nx + + % movie over peristimulus time + %------------------------------------------------------------------ + subplot(2,1,1) + for j = 1:4 + S{j} = zeros(size(bird)); + end + for f1 = 1:Ns(1) + for f2 = 1:Ns(2) + for f3 = 1:Ns(3) + for f4 = 1:Ns(4) + + % latent cues for this hidden state + %-------------------------------------------------- + if f1 == 1, a = {'bird','cats';'null','null'}; end + if f1 == 2, a = {'bird','seed';'null','null'}; end + if f1 == 3, a = {'bird','null';'null','seed'}; end + + % flip cues according to hidden (invariants) states + %-------------------------------------------------- + if f3 == 2, a = flipud(a); end + if f4 == 2, a = fliplr(a); end + + % mixture + %-------------------------------------------------- + p = MDP.xn{1}(Ni(i),f1,1,k)*MDP.xn{3}(Ni(i),f3,1,k)*MDP.xn{4}(Ni(i),f4,1,k); + for j = 1:4 + S{j} = S{j} + eval(a{j})*p; + end + end + end + end + end + + % image + %------------------------------------------------------------------ + hold off + for j = 1:numel(S) + imagesc(r + x(j + 1,1),r + x(j + 1,2),S{j}/max(S{j}(:))), hold on + end + + % stimulus + %------------------------------------------------------------------ + d = (1 - exp(1 - i)); + if MDP.o(1,k) == 1 + imagesc(r,r,null.*mask*d) + elseif MDP.o(1,k) == 2 + imagesc(r,r,bird.*mask*d) + elseif MDP.o(1,k) == 3 + imagesc(r,r,seed.*mask*d) + elseif MDP.o(1,k) == 4 + imagesc(r,r,cats.*mask*d) + end + + % save + %------------------------------------------------------------------ + axis image, axis([-2,2,-2,2]), drawnow + M((k - 1)*Nx + i) = getframe(gca); + + end + + + % static pictures + %---------------------------------------------------------------------- + subplot(2,Ne,Ne + k),hold off + for j = 1:numel(S) + imagesc(r + x(j + 1,1),r + x(j + 1,2),S{j}/max(S{j}(:))), hold on + end + + % stimulus + %------------------------------------------------------------------ + if MDP.o(1,k) == 1 + imagesc(r,r,null.*mask) + elseif MDP.o(1,k) == 2 + imagesc(r,r,bird.*mask) + elseif MDP.o(1,k) == 3 + imagesc(r,r,seed.*mask) + elseif MDP.o(1,k) == 4 + imagesc(r,r,cats.*mask) + end + + for j = 1:k + X(j,:) = x(MDP.o(2,j),:); + end + plot(X(:,1),X(:,2),'b.','MarkerSize',8) + + % save + %------------------------------------------------------------------ + axis image, axis([-2,2,-2,2]), drawnow + +end + +% Extract and plot eye movements +%-------------------------------------------------------------------------- +subplot(2,1,1) +set(gca,'Userdata',{M,16}) +set(gca,'ButtonDownFcn','spm_DEM_ButtonDownFcn') +title('Scene construction','FontSize',16) +title('Percept (click axis for movie)') diff --git a/toolbox/DEM/DEM_demo_MDP_search.m b/toolbox/DEM/DEM_demo_MDP_search.m new file mode 100644 index 00000000..febdd8e7 --- /dev/null +++ b/toolbox/DEM/DEM_demo_MDP_search.m @@ -0,0 +1,536 @@ +function MDP = DEM_demo_MDP_search +% Demo of active inference for visual salience +%__________________________________________________________________________ +% +% This routine uses active inference for Markov decision processes to +% illustrate epistemic foraging in the context of visual searches. Here, +% the agent has to categorise scenes on the basis of the relative position +% of various cues. Crucially, the agent can only sample one cue or location +% at a time and therefore has to accumulate evidence for competing +% hypotheses. This rests upon resolving uncertainty about which scene or +% hypothesis is in play through the minimisation of expected free energy. +% +% When the agent become sufficiently confident about the underlying scene, +% it then makes a saccade to a choice location - to obtain feedback (right +% or wrong). The agent prefers to be right and does not expect to be +% wrong. We first illustrate a single trial in terms of behaviour and +% electrophysiological responses. We then consider sequences of trials and +% how average behaviour (accuracy, number of saccades and saccade duration) +% depends upon prior preferences and prior precision. +% +% This demonstration uses a factorised version of the MDP scheme. In +% other words, we assume a mean field approximation to the posterior over +% different hidden states (context, location, scene reflection) – and over +% multiple modalities (what versus where). This provides a parsimonious +% representation of posterior beliefs over hidden states – but does induce +% degree of overconfidence associated with approximate Bayesian inference. +% +% see also: DEM_demo_MDP_habits.m and spm_MPD_VB_X.m +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: DEM_demo_MDP_search.m 6665 2016-01-08 21:05:13Z karl $ + +% set up and preliminaries +%========================================================================== +% In this example, an agent has to categorise a scene that comprises +% potential cues at four peripheral locations, starting from a central +% fixation point. This involves a form of scene construction, in which the +% relationship between various cues determines the category of scene. In +% brief, the scene always contains a bird and seed, or bird and a cat. If +% the bird is next to the seed or the cat, then the scene is categorised as +% feed or flee respectively. Conversely, if the seed is in an opposite +% diagonal location, the category is wait. The particular positions of the +% cues are irrelevant, the important attribute are there relationships. +% This means hidden states have to include some spatial mappings that +% induce invariances to spatial transformations. These are reflections +% around the horizontal and vertical axes. +% +% There are two outcome modalities (what and where), encoding one of six +% cues and one of eight locations (there are three extra locations that +% provide feedback about the respective decision). The hidden states +% have four factors; corresponding to context (the three categories), where +% (the eight locations) and two further factors modelling invariance +%-------------------------------------------------------------------------- +rng('default') + +% prior beliefs about initial states (in terms of counts_: D and d +%-------------------------------------------------------------------------- +d{1} = [1 1 1]'; % what: {'flee','feed','wait'} +d{2} = [1 0 0 0 0 0 0 0]'; % where: {'start','1',...,'4','flee','feed','wait'} +d{3} = [1 1]'; % flip(ud): {'no','yes'} +d{4} = [1 1]'; % flip(rl): {'no','yes'} + + +% probabilistic mapping from hidden states to outcomes: A +%-------------------------------------------------------------------------- +Nf = numel(d); +for f = 1:Nf + Ns(f) = numel(d{f}); +end +No = [6 8]; +Ng = numel(No); +for g = 1:Ng + A{g} = zeros([No(g),Ns]); +end +for f1 = 1:Ns(1) + for f2 = 1:Ns(2) + for f3 = 1:Ns(3) + for f4 = 1:Ns(4) + + % location of cues for this hidden state + %---------------------------------------------------------- + if f1 == 1, a = {'bird','cat' ;'null','null'}; end + if f1 == 2, a = {'bird','seed';'null','null'}; end + if f1 == 3, a = {'bird','null';'null','seed'}; end + + % flip cues according to hidden (invariants) states + %---------------------------------------------------------- + if f3 == 2, a = flipud(a); end + if f4 == 2, a = fliplr(a); end + + % what: A{1} {'null','bird,'seed','cat','right','wrong'} + %========================================================== + if f2 == 1 + + % at fixation location + %---------------------------------------------------------- + A{1}(1,f1,f2,f3,f4) = true; + + elseif f2 > 1 && f2 < 6 + + % saccade to cue location + %---------------------------------------------------------- + A{1}(1,f1,f2,f3,f4) = strcmp(a{f2 - 1},'null'); + A{1}(2,f1,f2,f3,f4) = strcmp(a{f2 - 1},'bird'); + A{1}(3,f1,f2,f3,f4) = strcmp(a{f2 - 1},'seed'); + A{1}(4,f1,f2,f3,f4) = strcmp(a{f2 - 1},'cat'); + + elseif f2 > 5 + + % saccade choice location + %------------------------------------------------------ + A{1}(5,f1,f2,f3,f4) = (f2 - 5) == f1; + A{1}(6,f1,f2,f3,f4) = (f2 - 5) ~= f1; + + end + + % where: A{2} {'start','1',...,'4','flee','feed','wait'} + %---------------------------------------------------------- + A{2}(f2,f1,f2,f3,f4) = 1; + + end + end + end +end +for g = 1:Ng + A{g} = double(A{g}); +end + +% controlled transitions: B{f} for each factor +%-------------------------------------------------------------------------- +for f = 1:Nf + B{f} = eye(Ns(f)); +end + +% controllable fixation points: move to the k-th location +%-------------------------------------------------------------------------- +for k = 1:Ns(2) + B{2}(:,:,k) = 0; + B{2}(k,:,k) = 1; +end + + +% allowable policies (here, specified as the next action) U +%-------------------------------------------------------------------------- +Np = size(B{2},3); +U = ones(1,Np,Nf); +U(:,:,2) = 1:Np; + +% priors: (utility) C +%-------------------------------------------------------------------------- +T = 6; +C{1} = zeros(No(1),T); +C{2} = zeros(No(2),T); +C{1}(5,:) = 2; % the agent expects to be right +C{1}(6,:) = -4; % and not wrong + + +% MDP Structure - this will be used to generate arrays for multiple trials +%========================================================================== +mdp.T = T; % number of moves +mdp.U = U; % allowable policies +mdp.A = A; % observation model +mdp.B = B; % transition probabilities +mdp.C = C; % preferred outcomes +mdp.D = d; % prior over initial states +mdp.s = [1 1 1 1]'; % initial state +mdp.o = [1 1]'; % initial outcome + +mdp.Aname = {'what','where'}; +mdp.Bname = {'what','where','flip','flip'}; + + +% illustrate a single trial +%========================================================================== +MDP = spm_MDP_VB_X(mdp); + +% show belief updates (and behaviour) +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 1'); clf +spm_MDP_VB_trial(MDP); +subplot(3,2,3) +spm_MDP_search_plot(MDP) + + +% illustrate phase-precession and responses +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 2'); clf +spm_MDP_VB_LFP(MDP,[],1); + + +% illustrate evidence accumulation and perceptual synthesis +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 3'); clf +spm_MDP_search_percept(MDP) + +return + +% illustrate a sequence of trials +%========================================================================== + +% true initial states – with context change at trial 12 +%-------------------------------------------------------------------------- +clear MDP +N = 32; +s(1,:) = ceil(rand(1,N)*3); +s(2,:) = ceil(rand(1,N)*1); +s(3,:) = ceil(rand(1,N)*2); +s(4,:) = ceil(rand(1,N)*2); + +for i = 1:N + MDP(i) = mdp; % create structure array + MDP(i).s = s(:,i); % context +end + + +% Solve - an example sequence +%========================================================================== +MDP = spm_MDP_VB_X(MDP); + +% illustrate behavioural responses and neuronal correlates +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 4'); clf +spm_MDP_VB_game(MDP); + +% illustrate phase-amplitude (theta-gamma) coupling +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 5'); clf +spm_MDP_VB_LFP(MDP(1:8)); + +% illustrate behaviour in more detail +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 6'); clf; +n = 3; +for i = 1:n*n + subplot(n,n,i), spm_MDP_search_plot(MDP(i)); +end + + +% illustrate the effects of epistemic and incentive salience +%========================================================================== +mdp.beta = 1; +mdp.T = 8; % maximum number of saccades + +c = (0:8)/8; +c = -4*c; +for j = 1:length(c) + + % array of trials + %---------------------------------------------------------------------- + clear MDP + for i = 1:N + MDP(i) = mdp; % create structure array + MDP(i).C{1}(6,:) = c(j); % salience + MDP(i).s = s(:,i); % context + end + + % solve and evaluate performance + %---------------------------------------------------------------------- + MDP = spm_MDP_VB_X(MDP); + for i = 1:N + o = MDP(i).o(1,:); % outcomes + P(1,i) = double(any(o == 5) & ~any(o == 6)); % accuracy + P(2,i) = find([(o > 4), 1],1) - 1; % number of saccades + P(3,i) = mean(MDP(i).rt); % reaction time + end + + % solve and evaluate performance + %---------------------------------------------------------------------- + EQ(:,j) = mean(P,2); +end + +spm_figure('GetWin','Figure 7'); clf; + +subplot(3,3,1), bar(-c,EQ(1,:)*100) +xlabel('Prior preference'),ylabel('percent correct') +title('Accuracy','Fontsize',16), axis square + +subplot(3,3,2), bar(-c,EQ(2,:)) +xlabel('Prior preference'),ylabel('number of saccades') +title('Decision time','Fontsize',16), axis square + +subplot(3,3,3), bar(-c,EQ(3,:)) +xlabel('Prior preference'),ylabel('saccade duration') +title('Reaction time','Fontsize',16), axis square + +% now repeat but changing prior precision +%-------------------------------------------------------------------------- +b = (0:8)/8; +b = 1./(2*b + 1/8); +for j = 1:length(b) + + % array of trials + %---------------------------------------------------------------------- + clear MDP + for i = 1:N + MDP(i) = mdp; % create structure array + MDP(i).beta = b(j); % prior (inverse) precision + MDP(i).s = s(:,i); % context + end + + % solve and evaluate performance + %---------------------------------------------------------------------- + MDP = spm_MDP_VB_X(MDP); + for i = 1:N + o = MDP(i).o(1,:); % outcomes + P(1,i) = double(any(o == 5) & ~any(o == 6)); % accuracy + P(2,i) = find([(o > 4), 1],1) - 1; % number of saccades + P(3,i) = mean(MDP(i).rt); % reaction time + end + + % solve and evaluate performance + %---------------------------------------------------------------------- + EP(:,j) = mean(P,2); +end + +subplot(3,3,4), bar(1./b,EP(1,:)*100) +xlabel('Prior precision'),ylabel('percent correct') +title('Accuracy','Fontsize',16), axis square + +subplot(3,3,5), bar(1./b,EP(2,:)) +xlabel('Prior precision'),ylabel('number of saccades') +title('Decision time','Fontsize',16), axis square + +subplot(3,3,6), bar(1./b,EP(3,:)) +xlabel('Prior precision'),ylabel('saccade duration') +title('Reaction time','Fontsize',16), axis square + +save + +return + + + + +function spm_MDP_search_plot(MDP) +% illustrates visual search graphically +%-------------------------------------------------------------------------- + +% locations +%-------------------------------------------------------------------------- +x = [0,0;-1 -1; -1 1; 1 -1;1 1;-1,2.5;0,2.5;1,2.5]; +y = x + 1/4; +r = [-1,1]/2; + +% plot cues +%-------------------------------------------------------------------------- +if strcmp('replace',get(gca,'Nextplot')) + + % load images + %---------------------------------------------------------------------- + load MDP_search_graphics + null = zeros(size(bird)) + 1; + f = MDP.s(:,1); + + % latent cues for this hidden state + %---------------------------------------------------------------------- + if f(1) == 1, a = {'bird','cats';'null','null'}; end + if f(1) == 2, a = {'bird','seed';'null','null'}; end + if f(1) == 3, a = {'bird','null';'null','seed'}; end + + % flip cues according to hidden (invariants) states + %---------------------------------------------------------------------- + if f(3) == 2, a = flipud(a); end + if f(4) == 2, a = fliplr(a); end + + for i = 1:numel(a) + image(r + x(i + 1,1),r + x(i + 1,2),eval(a{i})), hold on + end + + % choices + %---------------------------------------------------------------------- + choice = {'flee','feed','wait'}; + for i = 1:3 + if i == f(1) + text(i - 2,2.5,choice{i},'FontWeight','Bold','color','red') + else + text(i - 2,2.5,choice{i}) + end + + end + axis image, axis([-2,2,-2,3]) + + % labels + %---------------------------------------------------------------------- + for i = 1:size(x,1) + text(y(i,1),y(i,2),num2str(i),'FontSize',12,'FontWeight','Bold','color','g') + end +end + +% Extract and plot eye movements +%-------------------------------------------------------------------------- +for i = 1:numel(MDP.o(2,:)) + X(i,:) = x(MDP.o(2,i),:); +end +for j = 1:2 + T(:,j) = interp(X(:,j),8,2); + T(:,j) = T(:,j) + spm_conv(randn(size(T(:,j))),2)/16; +end +plot(T(:,1),T(:,2),'b:') +plot(X(:,1),X(:,2),'b.','MarkerSize',8) + +function spm_MDP_search_percept(MDP) +% illustrates visual search graphically +%-------------------------------------------------------------------------- + +% load images +%-------------------------------------------------------------------------- +load MDP_search_graphics +clf + +null = zeros(size(bird)) + 1; +mask = hamming(256); +mask = mask*mask'; +for i = 1:3 + mask(:,:,i) = mask(:,:,1); +end +x = [0,0;-1 -1; -1 1; 1 -1;1 1;-1,2.5;0,2.5;1,2.5]; +r = [-1,1]/2; +try + d = MDP.D; +catch + d = MDP.d; +end +Nf = numel(d); +for f = 1:Nf + Ns(f) = numel(d{f}); +end + +% plot cues +%-------------------------------------------------------------------------- +Ni = 1:size(MDP.xn{1},1); +Nx = length(Ni); +Ne = find([MDP.o(1,:) > 4,1],1) - 1; +for k = 1:Ne + for i = 1:Nx + + % movie over peristimulus time + %------------------------------------------------------------------ + subplot(2,1,1) + for j = 1:4 + S{j} = zeros(size(bird)); + end + for f1 = 1:Ns(1) + for f2 = 1:Ns(2) + for f3 = 1:Ns(3) + for f4 = 1:Ns(4) + + % latent cues for this hidden state + %-------------------------------------------------- + if f1 == 1, a = {'bird','cats';'null','null'}; end + if f1 == 2, a = {'bird','seed';'null','null'}; end + if f1 == 3, a = {'bird','null';'null','seed'}; end + + % flip cues according to hidden (invariants) states + %-------------------------------------------------- + if f3 == 2, a = flipud(a); end + if f4 == 2, a = fliplr(a); end + + % mixture + %-------------------------------------------------- + p = MDP.xn{1}(Ni(i),f1,1,k)*MDP.xn{3}(Ni(i),f3,1,k)*MDP.xn{4}(Ni(i),f4,1,k); + for j = 1:4 + S{j} = S{j} + eval(a{j})*p; + end + end + end + end + end + + % image + %------------------------------------------------------------------ + hold off + for j = 1:numel(S) + imagesc(r + x(j + 1,1),r + x(j + 1,2),S{j}/max(S{j}(:))), hold on + end + + % stimulus + %------------------------------------------------------------------ + d = (1 - exp(1 - i)); + if MDP.o(1,k) == 1 + imagesc(r,r,null.*mask*d) + elseif MDP.o(1,k) == 2 + imagesc(r,r,bird.*mask*d) + elseif MDP.o(1,k) == 3 + imagesc(r,r,seed.*mask*d) + elseif MDP.o(1,k) == 4 + imagesc(r,r,cats.*mask*d) + end + + % save + %------------------------------------------------------------------ + axis image, axis([-2,2,-2,2]), drawnow + M((k - 1)*Nx + i) = getframe(gca); + + end + + + % static pictures + %---------------------------------------------------------------------- + subplot(2,Ne,Ne + k),hold off + for j = 1:numel(S) + imagesc(r + x(j + 1,1),r + x(j + 1,2),S{j}/max(S{j}(:))), hold on + end + + % stimulus + %------------------------------------------------------------------ + if MDP.o(1,k) == 1 + imagesc(r,r,null.*mask) + elseif MDP.o(1,k) == 2 + imagesc(r,r,bird.*mask) + elseif MDP.o(1,k) == 3 + imagesc(r,r,seed.*mask) + elseif MDP.o(1,k) == 4 + imagesc(r,r,cats.*mask) + end + + for j = 1:k + X(j,:) = x(MDP.o(2,j),:); + end + plot(X(:,1),X(:,2),'b.','MarkerSize',8) + + % save + %------------------------------------------------------------------ + axis image, axis([-2,2,-2,2]), drawnow + +end + +% Extract and plot eye movements +%-------------------------------------------------------------------------- +subplot(2,1,1) +set(gca,'Userdata',{M,16}) +set(gca,'ButtonDownFcn','spm_DEM_ButtonDownFcn') +title('Scene construction','FontSize',16) +title('Percept (click axis for movie)') diff --git a/toolbox/DEM/DEM_demo_ontology.m b/toolbox/DEM/DEM_demo_ontology.m new file mode 100644 index 00000000..1bab1679 --- /dev/null +++ b/toolbox/DEM/DEM_demo_ontology.m @@ -0,0 +1,259 @@ +function DEM_demo_ontology +% This demonstration routine illustrates how a generative model can be used +% to furnish a computational nosology. In brief, it generates symptoms and +% diagnostic profiles from hidden or latent exogenous causes (e.g., +% therapeutic interventions) that are mediated by latent (pathophysiological +% and psychopathological) states. Pathophysiological trajectories are +% modelled with a Lorenz attractor that (with a linear mapping) +% produces (two-dimensional) psychopathology. In turn, the +% psychopathological states generate symptoms (with a non-linear function +% of linear mixtures) and diagnostic outcomes (with a softmax function of +% diagnostic potential). The psychopathological state of a subject is +% associated with a diagnostic potential in terms of its Euclidean distance +% from disease categories (locations in the associated state space). +% +% We start by simulating a relapsing-remitting disease process and then +% infer the latent states and parameters of a particular subject. +% This is then repeated in the setting of a therapeutic intervention. +% The demonstration then briefly considers model identification and +% selection by focusing on the mapping between pathophysiology and +% psychopathology. Finally, We consider, prognosis and prediction by +% estimating subject-specific parameters prior to therapy and then +% predicting putative response in the future, based upon a posterior +% predictive density. +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: DEM_demo_ontology.m 6511 2015-08-02 15:05:41Z karl $ + + +% Set up the generative model +%========================================================================== +rng('default') + +% length of trajectory +%-------------------------------------------------------------------------- +N = 64; % number of assessments + +% serial correlations and other inversion parameters +%-------------------------------------------------------------------------- +M(1).E.s = 1/2; +M(1).E.n = 2; +M(1).E.K = 1/16; +M(1).E.nE = 32; + +% therapeutic intervention; ranging between zero and one, starting halfway +% through the patient assessment +%-------------------------------------------------------------------------- +U = spm_phi(((1:N) - N/2)); + + +% level 1: the level that generates diagnostic and symptom profiles g(v) from +% latent (psychopathological) causes (v) +%-------------------------------------------------------------------------- +P.A = randn(6,2)/32; +P.B = [0 0 1 1; 0 1 0 1]; + +M(1).g = @(x,v,P)[spm_softmax(-sum((P.B - v*ones(1,4)).^2)'); tanh(P.A*exp(v))]; +M(1).pE = P; +M(1).V = exp(12); + + +% level 2: the level that generates latent causes v = g(x) from (pathophysiological) +% states (x) that are subject to interventions (U) +%-------------------------------------------------------------------------- +P.A = [10 24 1]; +P.B = [2 0;1/2 1]; + +M(2).f = @(x,v,P)[-P.A(1) P.A(1) 0; ((1 - v*P.A(3))*P.A(2) - x(3)) -1 0; x(2) 0 -8/3]*x/32; +M(2).g = @(x,v,P) P.B*x([2 3])/16; +M(2).x = [2; 4; 32]; +M(2).pE = P; +M(2).pC = diag([1 1 1 0 0 0 0]); +M(2).V = exp(8); +M(2).W = exp(4); + +% level 3: the exogenous (therapeutic) interventions +%-------------------------------------------------------------------------- +M(3).v = U(:,1); +M(3).V = exp(16); + + +% illustrate trajectories with and without therapy +%========================================================================== + +% set subject-specific parameters: parameters of the attractor in P{2}.A +%-------------------------------------------------------------------------- +P = {M.pE}; +P{2}.A(2) = 32; + +% natural progression without therapy: DEM.U = U*0 +%-------------------------------------------------------------------------- +DEM.U = U*0; +DEM = spm_DEM_generate(M,DEM.U,P); +DEM = spm_DEM(DEM); + +spm_figure('GetWin','Figure 1'); clf +spm_DEM_plot(DEM) + +% repeat with therapy: DEM.U = U +%-------------------------------------------------------------------------- +DEM.U = U; +DEM = spm_DEM_generate(M,DEM.U,P); +DEM = spm_DEM(DEM); + +% plot true and inferred trajectories +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 2'); clf +spm_DEM_plot(DEM) + + +% prognosis and prediction +%========================================================================== + +% Estimate subject-specific parameters at presentation (1:N/2 assessments) +%-------------------------------------------------------------------------- +PEM = DEM; +PEM.U = PEM.U(:,1:N/2); +PEM.Y = PEM.Y(:,1:N/2); +PEM = spm_DEM(PEM); + +% plot true and inferred parameters +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 3'); clf +spm_DEM_qP(PEM.qP,PEM.pP) + +% update priors and predict with zero precision data +%-------------------------------------------------------------------------- +PEM = spm_ADEM_update(PEM,0); +PEM.M(1).V = 0; +PEM.M(2).W = exp(8); +PEM.M(2).pC = 0; + +PEM.U = U(N/4:end); +PEM.Y = zeros(size(PEM.Y,1),size(PEM.U,2)); +PEM = spm_DEM(PEM); + +% plot predicted trajectories +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 4'); clf +spm_DEM_plot(PEM) + +% now repeat without therapeutic intervention +%========================================================================== +PEM.U = U(N/4:end)*0; +PEM = spm_DEM(PEM); + +spm_figure('GetWin','Figure 5'); clf +spm_DEM_plot(PEM) + +% overlay trajectories with increasing levels of therapy (u) +%-------------------------------------------------------------------------- +u = linspace(0,2,8); +for i = 1:length(u) + + % predict a response to treatment level + %---------------------------------------------------------------------- + PEM.U = U(N/4:end)*u(i); + PEM = spm_DEM(PEM); + + % plot response trajectory + %---------------------------------------------------------------------- + spm_figure('GetWin','Figure 5'); subplot(3,2,6), hold on + plot(PEM.qU.v{2}(1,:),PEM.qU.v{2}(2,:),'r:') + + % record predictive efficacy of treatment + %---------------------------------------------------------------------- + R(i) = PEM.qU.v{1}(1,end); + +end + +% show implicit dose-response relationships +%-------------------------------------------------------------------------- +spm_figure('GetWin','Figure 5'); subplot(3,2,5), hold off +bar(u,R) +title('dose response curve','fontsize',16) +xlabel('level of therapeutic intervention') +xlabel('probability of remission') + +% return + + +% model identification and selection +%========================================================================== +% in this section, we assume a cohort of subjects with known +% pathophysiology have been identified and estimate the generic parameters +% mapping pathophysiology to psychopathology. These estimates are then +% entered into any empirical Bayesian analysis to perform Bayesian model +% comparison to see which of these parameters are necessary. By +% construction, the parameter linking the first pathological state to the +% second psychopathology is redundant. +%-------------------------------------------------------------------------- +n = 8; % number of subjects +for i = 1:n + DCM{i,1} = spm_DEM_generate(M,U); + DCM{i,1}.U = U; + DCM{i,1}.M(2).pE.B = eye(2); + DCM{i,1}.M(2).pC = diag([0 0 0 1 1 1 1]); +end + +% Bayesian model inversion and hierarchical (empirical Bayesian) modelling +%-------------------------------------------------------------------------- +DCM = spm_dcm_fit(DCM); +PEB = spm_dcm_peb(DCM); + +% Bayesian model comparison using Bayesian model reduction +%-------------------------------------------------------------------------- +spm_dcm_bmr_all(PEB) +subplot(3,2,3), hold on, bar(1:4,M(2).pE.B(:),1/3), hold off + + + + +% plotting sub function +%========================================================================== +function spm_DEM_plot(DEM) + +% plot hidden states and causes +%-------------------------------------------------------------------------- +if isfield(DEM,'pU') + spm_DEM_qU(DEM.qU,DEM.pU) +else + spm_DEM_qU(DEM.qU) +end + +% supplement with trajectories +%-------------------------------------------------------------------------- +subplot(6,2,2), imagesc(DEM.qU.v{1}(1:4,:)) +title('diagnostic & symptom profile ','fontsize',16) +subplot(6,2,4), imagesc(DEM.qU.v{1}(5:end,:)) + +% supplement with trajectories +%-------------------------------------------------------------------------- +a = [-1 3 -1 3]; +vi = linspace(a(1),a(2),64); +vj = linspace(a(3),a(4),64); +for i = 1:length(vi) + for j = 1:length(vj) + x = DEM.M(1).x; + v = [vi(i); vj(j)]; + p = DEM.M(1).g(x,v,DEM.qP.P{1}); + p = p(1:4); + s(j,i) = p'*log(p); + [p,q] = max(p); + d(j,i) = q; + end +end +d = d.*(min(s(:)) - s); + +subplot(3,2,6), imagesc(a(1:2),a(3:4),d), axis xy, hold on +plot(DEM.qU.v{2}(1,:),DEM.qU.v{2}(2,:),'r'), hold off +title('latent psychopathology','fontsize',16) + + +subplot(3,2,4),title('latent pathophysiology','fontsize',16) +subplot(3,2,3),title('latent psychopathology','fontsize',16) +subplot(3,2,1),title('predicted symptoms and error','fontsize',16) +subplot(3,2,5),title('pathology and therapy','fontsize',16) +set(gca,'YLim',[-.2 1.2]) diff --git a/toolbox/DEM/DEM_evidence_accumulation.m b/toolbox/DEM/DEM_evidence_accumulation.m index 8159fa4d..5ddb2bfe 100644 --- a/toolbox/DEM/DEM_evidence_accumulation.m +++ b/toolbox/DEM/DEM_evidence_accumulation.m @@ -20,7 +20,7 @@ % Copyright (C) 2011 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: DEM_evidence_accumulation.m 4865 2012-08-28 12:46:50Z karl $ +% $Id: DEM_evidence_accumulation.m 6587 2015-11-02 10:29:49Z karl $ % hidden causes and states @@ -96,7 +96,7 @@ subplot(2,2,2) spm_plot_ci(DEM.qU.x,DEM.qU.S,pst,3,'exp'), hold on -plot(pst,exp(DEM.qU.x{1}),'LineWidth',1), hold off +plot(pst,exp(DEM.qU.x{1}),'LineWidth',1), hold off axis([1 N 0 2]) axis square diff --git a/toolbox/DEM/FEP_Manifold.m b/toolbox/DEM/FEP_Manifold.m index b1acd0fa..7be65b11 100644 --- a/toolbox/DEM/FEP_Manifold.m +++ b/toolbox/DEM/FEP_Manifold.m @@ -1,5 +1,4 @@ function FEP_Manifold -% FORMAT FEP_Manifold % This demonstration routine simulates the emergence of life - as defined % in terms of active inference - using a synthetic primordial soup. The key % aspect of this dynamics is that there is a separation between dynamical @@ -31,7 +30,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: FEP_Manifold.m 5709 2013-10-22 11:07:29Z guillaume $ +% $Id: FEP_Manifold.m 6655 2015-12-23 20:21:27Z karl $ % default settings (GRAPHICS sets movies) diff --git a/toolbox/DEM/MDP_search_graphics.mat b/toolbox/DEM/MDP_search_graphics.mat new file mode 100644 index 00000000..2dd408d7 Binary files /dev/null and b/toolbox/DEM/MDP_search_graphics.mat differ diff --git a/toolbox/DEM/spm_MDP_DP.m b/toolbox/DEM/spm_MDP_DP.m index 95faa7d9..511ccaba 100644 --- a/toolbox/DEM/spm_MDP_DP.m +++ b/toolbox/DEM/spm_MDP_DP.m @@ -1,6 +1,6 @@ -function [B0,BV] = spm_MDP_DP(MDP,OPTION) +function [B0,BV] = spm_MDP_DP(MDP) % dynamic programming using active inference -% FORMAT [MDP] = spm_MDP_DP(MDP,OPTION,W) +% FORMAT [B0,BV] = spm_MDP_DP(MDP) % % MDP.A(O,N) - Likelihood of O outcomes given N hidden states % MDP.B{M}(N,N) - transition probabilities among hidden states (priors) @@ -8,29 +8,24 @@ % % MDP.V(T - 1,P) - P allowable policies (control sequences) % -% OPTION - {'Free Energy' | 'KL Control' | 'Expected Utility'}; -% % B0 - optimal state action policy or transition matrix % BV - corresponding policy using value iteration %__________________________________________________________________________ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_MDP_DP.m 6451 2015-05-26 09:26:03Z karl $ +% $Id: spm_MDP_DP.m 6598 2015-11-11 19:48:30Z karl $ % set up and preliminaries %========================================================================== -% options -%-------------------------------------------------------------------------- -if nargin < 2, OPTION = 'Free Energy'; end % generative model and initial states %-------------------------------------------------------------------------- T = size(MDP.V,1) + 1; % number of outcomes Ns = size(MDP.B{1},1); % number of hidden states Nu = size(MDP.B,2); % number of hidden controls -p0 = exp(-16); % smallest probability +p0 = exp(-8); % smallest probability % likelihood model (for a partially observed MDP implicit in G) %-------------------------------------------------------------------------- @@ -46,31 +41,35 @@ % transition probabilities (priors) %-------------------------------------------------------------------------- for j = 1:Nu - B{j} = MDP.B{1,j} + p0; + B{j} = MDP.B{j} + p0; B{j} = B{j}*diag(1./sum(B{j})); + sB{j} = B{j}; + rB{j} = spm_softmax(log(B{j})'); lnB{j} = log(B{j}); end -% terminal probabilities (priors) + +% terminal probabilities over outcomes (priors) %-------------------------------------------------------------------------- try - C = MDP.C + p0; - if size(C,2) ~= T - C = C(:,end)*ones(1,T); - end + C = MDP.C; catch - C = ones(Ns,T); + C = zeros(No,1); end -C = C*diag(1./sum(C)); -lnC = log(C); + +% asume constant preferences over states +%-------------------------------------------------------------------------- +if size(C,2) ~= T + C = C(:,end)*ones(1,T); +end +C = A'*diag(1./sum(A,2))*spm_softmax(C); +C = log(C); % policies, states and their expectations %-------------------------------------------------------------------------- V = MDP.V; Np = size(V,2); % number of allowable policies - - % policy iteration %========================================================================== for s = 1:Ns @@ -78,32 +77,50 @@ % Variational iterations (hidden states) %====================================================================== - x = zeros(Ns,T,Np); + x = zeros(Ns,T,Np) + 1/Ns; for k = 1:Np - for i = 1:2 + % gradient descent on free energy + %------------------------------------------------------------------ + for i = 1:16 % hiddens states (x) %-------------------------------------------------------------- + x(:,1,k) = 0; x(s,1,k) = 1; - % future states - %-------------------------------------------------------------- - for j = 2:(T - 1) - v = lnB{V(j - 1,k)} *x(:,j - 1,k) + ... - lnB{V(j, k)}'*x(:,j + 1,k); - x(:,j,k) = spm_softmax(v); + for j = 2:T + + % current state + %---------------------------------------------------------- + xj = x(:,j,k); + qx = log(xj); + v = 0; + + % evaluate free energy and gradients (v = dFdx) + %---------------------------------------------------------- + if j > 1, v = v + qx - log(sB{V(j - 1,k)}*x(:,j - 1,k)); end + if j < T, v = v - log(rB{V(j ,k)}*x(:,j + 1,k)); end + + % update + %---------------------------------------------------------- + x(:,j,k) = spm_softmax(qx - v/4); + F(j,k) = xj'*v; + end - % last state + % convergence %-------------------------------------------------------------- - v = lnB{V(T - 1,k)} *x(:,T - 1,k); - x(:,T,k) = spm_softmax(v); + if i > 1 + dF = F0 - sum(F(:,k)); + if dF > 1/128, F0 = F0 - dF; else, break, end + else + F0 = sum(F(:,k)); + end end - end - + % value of policies (Q) %====================================================================== Q = zeros(Np,1); @@ -111,22 +128,9 @@ % path integral of expected free energy %------------------------------------------------------------------ - for j = 2:T - - switch OPTION - case{'Free Energy','FE'} - v = lnC(:,j) - log(x(:,j,k)) + H; - - case{'KL Control','KL'} - v = lnC(:,j) - log(x(:,j,k)); - - case{'Expected Utility','EU','RL'} - v = lnC(:,j); - - otherwise - disp(['unkown option: ' OPTION]) - end - Q(k) = Q(k) + v'*x(:,j,k); + for j = 2:T + v = C(:,j) - log(x(:,j,k)) + H; + Q(k) = Q(k) + v'*x(:,j,k); end end @@ -143,7 +147,7 @@ % value iteration %========================================================================== V = zeros(Ns,1); -lnC = lnC(:,end); +C = C(:,end); g = 1 - 1/T; for i = 1:32 for s = 1:Ns @@ -152,7 +156,7 @@ %------------------------------------------------------------------ Q = zeros(Nu,1); for k = 1:Nu - Q(k) = B{1,k}(:,s)'*(lnC + g*V); + Q(k) = B{1,k}(:,s)'*(C + g*V); end % optimal transition from this state @@ -164,7 +168,7 @@ % optimal transition from this state %---------------------------------------------------------------------- - dV = BV'*(lnC + g*V) - V; + dV = BV'*(C + g*V) - V; V = V + dV; % convergence diff --git a/toolbox/DEM/spm_MDP_VB.m b/toolbox/DEM/spm_MDP_VB.m index 38f4963b..9e142117 100644 --- a/toolbox/DEM/spm_MDP_VB.m +++ b/toolbox/DEM/spm_MDP_VB.m @@ -1,48 +1,48 @@ -function [MDP] = spm_MDP_VB(MDP,OPTION,W) -% action selection using active inference -% FORMAT [MDP] = spm_MDP_VB(MDP,OPTION,W) +function [MDP] = spm_MDP_VB(MDP,OPTIONS) +% active inference and learning using variational Bayes +% FORMAT [MDP] = spm_MDP_VB(MDP,OPTIONS) % -% MDP.N - number of variational iterations (default 4) % MDP.S(N,1) - true initial state +% MDP.V(T - 1,P) - P allowable policies (control sequences) % -% MDP.A(O,N) - Likelihood of O outcomes given N hidden states +% MDP.A(O,N) - likelihood of O outcomes given N hidden states % MDP.B{M}(N,N) - transition probabilities among hidden states (priors) -% MDP.C(N,1) - prior preferences (prior over future states) +% MDP.C(N,1) - prior preferences (prior over future outcomes) % MDP.D(N,1) - prior probabilities (prior over initial states) % -% MDP.V(T - 1,P) - P allowable policies (control sequences) +% MDP.a(O,N) - concentration parameters for A +% MDP.b{M}(N,N) - concentration parameters for B +% MDP.c(N,N) - concentration parameters for H +% MDP.d(N,1) - concentration parameters for D +% MDP.e(P,1) - concentration parameters for u % % optional: -% MDP.s(1 x T) - vector of true states - for deterministic solutions -% MDP.o(1 x T) - vector of observations - for deterministic solutions -% MDP.a(1 x T) - vector of action - for deterministic solutions -% MDP.w(1 x T) - vector of precisions - for deterministic solutions +% MDP.s(1,T) - vector of true states - for deterministic solutions +% MDP.o(1,T) - vector of observations - for deterministic solutions +% MDP.u(1,T) - vector of action - for deterministic solutions +% MDP.w(1,T) - vector of precisions - for deterministic solutions +% +% MDP.alpha - upper bound on precision (Gamma hyperprior – shape [1]) +% MDP.beta - precision over precision (Gamma hyperprior - rate [1/2]) % -% MDP.B{T,M}(N,N) - model transition probabilities for each time point -% MDP.G{T,M}(N,N) - true transition probabilities for each time point -% (default: MDP.G{T,M} = MDP.G{M} = MDP.B{M}) +% OPTIONS.plot - switch to suppress graphics: (default: [0]) +% OPTIONS.scheme - {'Free Energy' | 'KL Control' | 'Expected Utility'}; +% OPTIONS.habit - switch to suppress habit learning: (default: [1]) % -% MDP.plot - switch to suppress graphics: (default: [0]) -% MDP.alpha - upper bound on precision (Gamma hyperprior – shape [8]) -% MDP.beta - precision over precision (Gamma hyperprior - rate [1]) -% MDP.gamma - initial precision -% MDP.lamba - precision update rate % % produces: % -% MDP.P(M,T) - probability of emitting an action 1,...,M at time 1,...,T -% MDP.Q(N,T) - an array of conditional (posterior) expectations over -% N hidden states and time 1,...,T -% MDP.O(O,T) - a sparse matrix of ones encoding outcomes at time 1,...,T -% MDP.S(N,T) - a sparse matrix of ones encoding states at time 1,...,T -% MDP.U(M,T) - a sparse matrix of ones encoding action at time 1,...,T -% MDP.W(1,T) - posterior expectations of precision -% MDP.da - simulated dopamine responses (deconvolved) -% MDP.KLx - updating as scored with KL (state estimation) -% MDP.KLu - updating as scored with KL (policy selection) +% MDP.P(M,T) - probability of emitting action 1,...,M at time 1,...,T +% MDP.Q(N,T) - an array of conditional (posterior) expectations over +% N hidden states and time 1,...,T +% MDP.X - and Bayesian model averages over policies +% MDP.R - conditional expectations over policies % -% OPTION - {'Free Energy' | 'KL Control' | 'Expected Utility'}; -% W - optional fixed precision +% MDP.un - simulated neuronal encoding of hidden states +% MDP.xn - simulated neuronal encoding of policies +% MDP.wn - simulated neuronal encoding of precision (tonic) +% MDP.dn - simulated dopamine responses (phasic) +% MDP.rt - simulated reaction times % % This routine provides solutions of active inference (minimisation of % variational free energy) using a generative model based upon a Markov @@ -52,9 +52,7 @@ % dynamics are given by transition probabilities among states and the % likelihood corresponds to a particular outcome conditioned upon % hidden states. For simplicity, this routine assumes that action -% and hidden controls are isomorphic. If the dynamics of transition -% probabilities of the true process are not provided, this routine will use -% the equivalent probabilities from the generative model. +% and hidden controls are isomorphic. % % This implementation equips agents with the prior beliefs that they will % maximise expected free energy: expected free energy is the free energy @@ -68,28 +66,28 @@ % % This particular scheme is designed for any allowable policies or control % sequences specified in MDP.V. Constraints on allowable policies can limit -% the numerics or combinatorics considerable. For example, situations in +% the numerics or combinatorics considerably. For example, situations in % which one action can be selected at one time can be reduced to T polices % – with one (shift) control being emitted at all possible time points. % This specification of polices simplifies the generative model, allowing a % fairly exhaustive model of potential outcomes – eschewing a mean field -% approximation over successive control states. In brief, the agent simply -% represents the current state and states in the immediate and distant -% future. +% approximation over successive control states. In brief, the agent encodes +% beliefs about hidden states in the past and in the future conditioned +% on each policy (and a non-sequential state-state policy called a +% habit). These conditional expectations are used to evaluate the (path +% integral) of free energy that then determines the prior over policies. +% This prior is used to create a predictive distribution over outcomes, +% which specifies the next action. % -% The transition probabilities are a cell array of probability transition -% matrices corresponding to each (discrete) the level of the control state. -% -% Mote that the conditional expectations are functions of time but also -% contain expectations about fictive states over time at each time point. -% To create time dependent transition probabilities, one can specify a -% function in place of the transition probabilities under different levels -% of control. -% -% Partially observed Markov decision processes can be modelled by -% specifying a likelihood (as part of a generative model) and absorbing any -% probabilistic mapping between hidden states and outcomes -% into the transition probabilities G. +% In addition to state estimation and policy selection, the scheme also +% updates model parameters; including the state transition matrices, +% mapping to outcomes and the initial state. This is useful for learning +% the context. In addition, by observing its own behaviour, the agent will +% automatically learn habits. Finally, by observing policies chosen over +% trials, the agent develops prior expectations or beliefs about what it +% will do. If these priors (over policies – that include the habit) render +% some policies unlikely (using an Ockham's window), they will not be +% evaluated. % % See also:spm_MDP, which uses multiple future states and a mean field % approximation for control states – but allows for different actions @@ -100,304 +98,399 @@ % over hidden states and those specified by preferences or prior beliefs. %__________________________________________________________________________ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging - + % Karl Friston -% $Id: spm_MDP_VB.m 6451 2015-05-26 09:26:03Z karl $ - -% set up and preliminaries +% $Id: spm_MDP_VB.m 6657 2015-12-31 17:59:31Z karl $ + + +% deal with a sequence of trials %========================================================================== - -% options and precision defaults + +% options %-------------------------------------------------------------------------- -try, PLOT = MDP.plot; catch, PLOT = 0; end -try, alpha = MDP.alpha; catch, alpha = 8; end -try, beta = MDP.beta; catch, beta = 4; end -try, g = MDP.gamma; catch, g = 1; end -try, lambda = MDP.lambda; catch, lambda = 0; end -try, N = MDP.N; catch, N = 4; end - -% options and number of outcomes +try, OPTIONS.habit; catch, OPTIONS.habit = 0; end +try, OPTIONS.plot; catch, OPTIONS.plot = 0; end +try, OPTIONS.gamma_u; catch, OPTIONS.gamma_u = 0; end +try, OPTIONS.gamma_s; catch, OPTIONS.gamma_s = 0; end + + +% if there are multiple trials ensure that parameters are updated %-------------------------------------------------------------------------- -T = size(MDP.V,1) + 1; -if nargin < 2, OPTION = 'Free Energy'; end -MDP.OPT = OPTION; -if nargin > 2 - MDP.w = zeros(1,T) + W; -end - - -% set up figure if necessary -%-------------------------------------------------------------------------- -if PLOT - if ishandle(PLOT) - figure(PLOT); clf - PLOT = 2; - else - spm_figure('GetWin','MDP'); clf +if length(MDP) > 1 + + OPTS = OPTIONS; + OPTS.plot = 0; + for i = 1:length(MDP) + + % update concentration parameters + %------------------------------------------------------------------ + if i > 1 + try, MDP(i).a = OUT(i - 1).a; end + try, MDP(i).b = OUT(i - 1).b; end + try, MDP(i).c = OUT(i - 1).c; end + try, MDP(i).d = OUT(i - 1).d; end + try, MDP(i).e = OUT(i - 1).e; end + end + + % solve this trial + %------------------------------------------------------------------ + OUT(i) = spm_MDP_VB(MDP(i),OPTS); + + end + MDP = OUT; + + % plot summary statistics - over trials + %---------------------------------------------------------------------- + if OPTIONS.plot + if ishandle(OPTIONS.plot) + figure(OPTIONS.plot); clf + else + spm_figure('GetWin','MDP'); clf + end + spm_MDP_VB_game(MDP) end + return end + + +% set up and preliminaries +%========================================================================== +V = MDP.V; % allowable policies (T - 1,Np) -% generative model and initial states +% numbers of transitions, policies and states %-------------------------------------------------------------------------- -Ns = size(MDP.B{1},1); % number of hidden states -Nb = size(MDP.B,1); % number of time-dependent probabilities -Nu = size(MDP.B,2); % number of hidden controls -p0 = exp(-16); % smallest probability +T = size(MDP.V,1) + 1; % number of transitions +Np = size(MDP.V,2); % number of allowable policies +Ns = size(MDP.B{1},1); % number of hidden states +Nu = size(MDP.B,2); % number of hidden controls +Nh = Np + 1; % index of habit +p0 = exp(-8); % smallest probability +q0 = 1/16; % smallest probability + +% parameters of generative model and policies +%========================================================================== + % likelihood model (for a partially observed MDP implicit in G) %-------------------------------------------------------------------------- try A = MDP.A + p0; - No = size(MDP.A,1); % number of outcomes + No = size(MDP.A,1); % number of outcomes catch A = speye(Ns,Ns) + p0; No = Ns; end -A = A*diag(1./sum(A)); % normalise -lnA = log(A); % log probabilities -H = sum(A.*lnA)'; % negentropy of observations - -% transition probabilities (priors) +A = spm_norm(A); % normalise + +% parameters (concentration parameters): A %-------------------------------------------------------------------------- -for i = 1:(T - 1) - for j = 1:Nu - if i == 1 || Nb == (T - 1) - B{i,j} = MDP.B{i,j} + p0; - B{i,j} = B{i,j}*diag(1./sum(B{i,j})); - else - B{i,j} = B{1,j}; - end - lnB{i,j} = log(B{1,j}); - end +if isfield(MDP,'a') + qA = MDP.a + q0; + qA = psi(qA ) - ones(Ns,1)*psi(sum(qA)); +else + qA = log(spm_norm(A)); end - -% terminal probabilities (priors) + +% transition probabilities (priors) %-------------------------------------------------------------------------- -try - C = MDP.C + p0; +for i = 1:Nu - % asume constant preferences if only final states are specified + B{i} = MDP.B{i} + p0; + B{i} = spm_norm(B{i}); + + % parameters (concentration parameters): B %---------------------------------------------------------------------- - if size(C,2) ~= T - C = C(:,end)*ones(1,T); + if isfield(MDP,'b') + b = MDP.b{i} + q0; + sB{i} = spm_norm(b ); + rB{i} = spm_norm(b'); + qB{i} = psi(b) - ones(Ns,1)*psi(sum(b)); + else + b = MDP.B{i} + p0; + sB{i} = spm_norm(b ); + rB{i} = spm_norm(b'); + qB{i} = log(b); end - -catch - C = ones(Ns,T); + +end + +% parameters (concentration parameters) - Habits +%-------------------------------------------------------------------------- +c0 = 0; +for j = 1:Nu + c0 = c0 + MDP.B{j}; end -C = C*diag(1./sum(C)); -lnC = log(C); +if ~isfield(MDP,'c') + MDP.c = c0; +end +c = MDP.c + q0; +sH = spm_norm(c ); +rH = spm_norm(c'); +qH = psi(c) - ones(Ns,1)*psi(sum(c)); -% initial probabilities (priors) + +% priors over initial hidden states - concentration parameters %-------------------------------------------------------------------------- -try - D = spm_vec(MDP.D) + p0; -catch - D = ones(Ns,1); +if isfield(MDP,'d') + d = MDP.d + q0; + qD = psi(d) - ones(Ns,1)*psi(sum(d)); +elseif isfield(MDP,'D') + d = MDP.D + q0; + qD = log(spm_norm(d)); +else + d = ones(Ns,1); + qD = psi(d) - ones(Ns,1)*psi(sum(d)); end -D = D/sum(D); -lnD = log(D); -% generative process (assume the true process is the same as the model) +% priors over policies - concentration parameters %-------------------------------------------------------------------------- try - G = MDP.G; + e = MDP.e + q0; catch - G = MDP.B; + e(1:Np,1) = 4; + e(Nh) = 1; end -Ng = size(G,1); -for i = 1:(T - 1) - for j = 1:Nu - if i == 1 || Ng == (T - 1) - G{i,j} = G{i,j} + p0; - G{i,j} = G{i,j}*diag(1./sum(G{i,j})); - else - G{i,j} = G{1,j}; - end - end +if ~OPTIONS.habit || (sum(MDP.c(:)) - sum(c0(:))) < 8; + e(Nh) = q0; end - -% pparameters (concentration parameters) +qE = psi(e) - ones(Nh,1)*psi(sum(e)); + +% prior preferences (log probabilities) : C %-------------------------------------------------------------------------- try - c = MDP.c; + Vo = MDP.C; catch - c = 0; - for j = 1:Nu - c = c + B{1,j}/Nu; - end + Vo = zeros(No,1); +end + +% assume constant preferences, if only final states are specified +%-------------------------------------------------------------------------- +if size(Vo,2) ~= T + Vo = Vo(:,end)*ones(1,T); end -B0 = psi(c) - ones(Ns,1)*psi(sum(c)); +Vo = log(spm_softmax(Vo)); +H = sum(spm_softmax(qA).*qA); -% policies and their expectations +% log preferences over states %-------------------------------------------------------------------------- -V = MDP.V; % allowable policies (T - 1,Np) -Np = size(V,2); % number of allowable policies -R = ones(1,Np); % policies in play +Vs = spm_norm(A')*spm_softmax(Vo); +Vs = log(Vs) + H'*ones(1,T); +Vs = log(spm_softmax(Vs)); + +% precision defaults +%-------------------------------------------------------------------------- +try, alpha = MDP.alpha; catch, alpha = 16; end +try, beta = MDP.beta; catch, beta = 1; end +try, eta = MDP.eta; catch, eta = 2; end + % initial states and outcomes %-------------------------------------------------------------------------- -[p q] = max(A*MDP.S(:,1)); % initial outcome (index) -s = find( MDP.S(:,1)); % initial state (index) -o = sparse(1,1,q,1,T); % observations (index) -S = sparse(s,1,1,Ns,T); % states sampled (1 in K vector) -O = sparse(q,1,1,No,T); % states observed (1 in K vector) -U = zeros(Nu,T - 1); % action selected (1 in K vector) -P = zeros(Nu,T - 1); % posterior beliefs about control -x = zeros(Ns,T,Np + 1); % expectations of hidden states -X = zeros(Ns,T + 1); % expectations of hidden states -u = zeros(Np + 1,T - 1); % expectations of hidden states -a = zeros(1, T - 1); % action (index) -W = zeros(1, T); % posterior precision +try + s = MDP.s(1); % initial state (index) +catch + s = 1; +end +try + o = MDP.o(1); % initial outcome (index) +catch + o = find(rand < cumsum(A(:,s)),1); +end +P = zeros(Nu,T - 1); % posterior beliefs about control +x = zeros(Ns,T,Nh) + 1/Ns; % expectations of hidden states | policy +X = zeros(Ns,T); % expectations of hidden states +u = zeros(Nh,T - 1); % expectations of policy +a = zeros(1, T - 1); % action (index) + +% initialise priors over states +%-------------------------------------------------------------------------- +for k = 1:Nh + x(:,1,k) = spm_softmax(qD); +end + +% expected rate parameter +%-------------------------------------------------------------------------- +qbeta = beta; % initialise rate parameters +qeta = eta - mean(sum(Vs,2)); % initialise rate parameters +gu = zeros(1,T) + 1/qbeta; % posterior precision (policy) +gx = zeros(Nh,T) + 1/qeta; % posterior precision (policy) +qeta = zeros(Nh,1) + qeta; -X(:,T + 1) = spm_softmax(H + C(:,end)); % solve %========================================================================== -KLx = zeros(1,T); % state updates -KLu = zeros(1,T); % policy updates -wn = zeros(T*N,1); % simulated DA responses -b = alpha/g; % expected rate parameter -for t = 1:T - - % Variational updates (hidden states) under habitual policies - %====================================================================== - k = Np + 1; - for i = 1:4 - - % past and future states - %------------------------------------------------------------------ - for j = 1:T - v = 0; - if j <= t, v = lnA(o(j),:)'; end - if j > 1, v = v + B0 *x(:,j - 1,k); end - if j < T, v = v + B0'*x(:,j + 1,k); end - x(:,j,k) = spm_softmax(v); - end - - end +Ni = 16; % number of VB iterations +rt = zeros(1,T); % reaction times +xn = zeros(Ni,Ns,T,T,Np) + 1/Ns; % history of state updates +un = zeros(Nh,T*Ni); % policy updates +wn = zeros(T*Ni,1); % simulated DA responses +p = 1:Nh; % allowable policies +for t = 1:T - % learning - %====================================================================== - if t > 1 - c = c + x(:,t,k)*x(:,t - 1,k)'; - end - B0 = psi(c) - ones(Ns,1)*psi(sum(c)); + % processing time and reset + %---------------------------------------------------------------------- + tstart = tic; + x = spm_softmax(log(x)/2); % Variational updates (hidden states) under sequential policies %====================================================================== - - % retain allowable policies (that are consistent with last action) - %---------------------------------------------------------------------- - if t > 1 - R = R & ismember(V(t - 1,:),a(t - 1)); - end - w = find(R); - for k = w - - xk = spm_vec(x(:,:,k)) + p0; - for i = 1:4 + F = zeros(Nh,T); + for k = p + for i = 1:Ni + g = 0; + px = x(:,:,k); for j = 1:T - v = 0; - if j <= t, v = lnA(o(j),:)'; end - if j > 1, v = v + lnB{j - 1,V(j - 1,k)} *x(:,j - 1,k); end - if j < T, v = v + lnB{j, V(j, k)}'*x(:,j + 1,k); end - x(:,j,k) = spm_softmax(v); + + % current state + %---------------------------------------------------------- + qx = log(x(:,j,k)); + + % transition probabilities (with attention) + %---------------------------------------------------------- + if OPTIONS.gamma_s + gB = gx(k,t)*Vs(:,j)*ones(1,Ns); + if k > Np + fB = spm_softmax(qH + gB); + bB = spm_softmax(qH' + gB); + else + if j > 1, fB = spm_softmax(qB{V(j - 1,k)} + gB); end + if j < T, bB = spm_softmax(qB{V(j ,k)}' + gB); end + end + else + + % transition probabilities (without attention) + %------------------------------------------------------ + if k > Np + fB = sH; + bB = rH; + else + if j > 1, fB = sB{V(j - 1,k)}; end + if j < T, bB = rB{V(j ,k)}; end + end + end + + % evaluate free energy and gradients (v = dFdx) + %---------------------------------------------------------- + v = qx; + if j <= t, v = v - qA(o(j),:)'; end + if j == 1, v = v - qD; end + if j > 1, v = v - log(fB*x(:,j - 1,k)); end + if j < T, v = v - log(bB*x(:,j + 1,k)); end + + % evaluate (attention) gradients (g = dFdg) + %---------------------------------------------------------- + g = g + Vs(:,j)'*x(:,j,k); + + % free energy and belief updating + %---------------------------------------------------------- + F(k,j) = -x(:,j,k)'*v; + px(:,j) = spm_softmax(qx - v/8); + + % record neuronal activity + %---------------------------------------------------------- + xn(i,:,j,t,k) = x(:,j,k); + + end + + % hidden state updates + %-------------------------------------------------------------- + x(:,:,k) = px; + + % precision (attention) updates + %-------------------------------------------------------------- + if OPTIONS.gamma_s + v = qeta(k) - eta + g; + qeta(k) = qeta(k) - v/2; + gx(k,t) = 1/qeta(k); + + % simulated cholinergic responses (at each iteration) + %---------------------------------------------------------- + n = (t - 1)*Ni + i; + ch(n,k) = gx(k,t); end + end - - % KL update – states - %------------------------------------------------------------------ - kx = spm_vec(x(:,:,k)) + p0; - KLx(t) = KLx(t) + kx'*(log(kx) - log(xk)); - end - % expected (negative) free energy of policies (Q) + % (negative path integral of) free energy of policies (Q) %====================================================================== - Q = zeros(Np + 1,1); - w = [w Np + 1]; - for k = w - - % path integral of expected free energy - %------------------------------------------------------------------ - for j = (t + 1):T - - switch OPTION - case{'Free Energy','FE'} - v = lnC(:,j) - log(x(:,j,k) + p0) + H; - - case{'KL Control','KL'} - v = lnC(:,j) - log(x(:,j,k) + p0); - - case{'Expected Utility','EU','RL'} - v = lnC(:,j); - - otherwise - disp(['unkown option: ' OPTION]) - end - Q(k) = Q(k) + v'*x(:,j,k); - + Q = zeros(Nh,T); + for k = p + for j = 1:T + qx = A*x(:,j,k); + Q(k,j) = qx'*(Vo(:,j) - log(qx)) + H*x(:,j,k); end end % variational updates - policies and precision %====================================================================== - for i = 1:N - n = (t - 1)*N + i; + F = sum(F,2); + Q = sum(Q,2); + p = p((F(p) - max(F(p))) > -3); + for i = 1:Ni % policy (u) %------------------------------------------------------------------ - v = W(t)*Q(w); - u(w,t) = spm_softmax(v); - un(w,n) = u(w,t); + qu = spm_softmax(qE(p) + gu(t)*Q(p) + F(p)); + pu = spm_softmax(qE(p) + gu(t)*Q(p)); - % precision (W) + % precision (gu) with free energy gradients (v = -dF/dw) %------------------------------------------------------------------ - if isfield(MDP,'w') - W(t) = MDP.w(t); + if OPTIONS.gamma_u + gu(t) = 1/beta; else - b = lambda*b + (1 - lambda)*(beta - u(w,t)'*Q(w)); - W(t) = alpha/b; + v = qbeta - beta + (qu - pu)'*Q(p); + qbeta = qbeta - v/2; + gu(t) = 1/qbeta; end - % simulated dopamine responses (precision as each iteration) + % simulated dopamine responses (precision at each iteration) %------------------------------------------------------------------ - wn(n,1) = W(t); + n = (t - 1)*Ni + i; + u(p,t) = qu; + wn(n,1) = gu(t); + un(p,n) = qu; end + - % KL update – policies - %---------------------------------------------------------------------- - if t > 1 - KLu(t) = u(:,t)'*(log(u(:,t) + p0) - log(u(:,t - 1) + p0)); - end - - % Baysian model averaging of hidden states over policies + % Bayesian model averaging of hidden states over policies %---------------------------------------------------------------------- for i = 1:T X(:,i) = squeeze(x(:,i,:))*u(:,t); end + % processing time + %---------------------------------------------------------------------- + rt(t) = toc(tstart); + % action selection and sampling of next state (outcome) %====================================================================== if t < T - - % posterior expectations (control) + + % posterior expectations about (remaining) actions (q) %================================================================== - lnx = log(X(:,t + 1)); - for j = 1:Nu - xu = B{t,j}*X(:,t); - P(j,t) = (H + lnx - log(xu))'*xu; + if numel(p) > 1 + q = unique(V(t,p(1:end - 1))); + else + q = 1:Nu; + end + v = log(A*X(:,t + 1)); + for j = q + qo = A*B{j}*X(:,t); + P(j,t) = (v - log(qo))'*qo + 16; end - P(:,t) = spm_softmax(P(:,t)); - % next action (the action that minimises expected free energy) + % action selection + %------------------------------------------------------------------ + P(:,t) = spm_softmax(alpha*P(:,t)); + + % next action %------------------------------------------------------------------ try - a(t) = MDP.a(t); + a(t) = MDP.u(t); catch try a(t) = find(rand < cumsum(P(:,t)),1); @@ -405,20 +498,16 @@ error('there are no more allowable policies') end end - - % save action - %------------------------------------------------------------------ - U(a(t),t) = 1; - + % next sampled state %------------------------------------------------------------------ try s(t + 1) = MDP.s(t + 1); catch - s(t + 1) = find(rand < cumsum(G{t,a(t)}(:,s(t))),1); + s(t + 1) = find(rand < cumsum(B{a(t)}(:,s(t))),1); end - % next obsverved state + % next observed state %------------------------------------------------------------------ try o(t + 1) = MDP.o(t + 1); @@ -428,114 +517,109 @@ % save outcome and state sampled %------------------------------------------------------------------ - W(1,t + 1) = W(t); - O(o(t + 1),t + 1) = 1; - S(s(t + 1),t + 1) = 1; + gu(1,t + 1) = gu(t); end + +end + +% learning +%========================================================================== +for t = 1:T + % mapping from hidden states to outcomes: a + %---------------------------------------------------------------------- + if isfield(MDP,'a') + i = MDP.a > 0; + da = sparse(o(t),1,1,No,1)*X(:,t)'; + MDP.a(i) = MDP.a + da(i); + end - % plot - %====================================================================== - if PLOT > 0 && (T > 3 || t == T) - - - % posterior beliefs about hidden states - %------------------------------------------------------------------ - subplot(4,2,1) - imagesc(1 - X),hold on - if size(X,1) > 128 - spm_spy(X,16,1) - end - plot(s,'.c','MarkerSize',16), hold off - title('Hidden states (and utility)','FontSize',14) - xlabel('trial','FontSize',12) - ylabel('Hidden state','FontSize',12) - - % posterior beliefs about control states - %================================================================== - subplot(4,2,2) - imagesc(1 - P), hold on - plot(a,'.c','MarkerSize',16), hold off - title('Inferred and selected action','FontSize',14) - xlabel('trial','FontSize',12) - ylabel('action','FontSize',12) - - % policies - %------------------------------------------------------------------ - subplot(4,2,3) - imagesc(MDP.V') - set(gca,'YLim',[0 (Np + 1)] + 1/2) - title('Allowable policies','FontSize',14) - ylabel('policy','FontSize',12) - xlabel('trial','FontSize',12) - - % expectations over policies - %------------------------------------------------------------------ - subplot(4,2,4) - imagesc(1 - un) - title('Posterior probability','FontSize',14) - ylabel('Policy','FontSize',12) - xlabel('updates','FontSize',12) - - % sample (observation) - %------------------------------------------------------------------ - subplot(4,2,5) - if size(O,1) > 128 - spm_spy(O,16,1) - else - imagesc(1 - O) + % mapping from hidden states to hidden states: b(u) + %---------------------------------------------------------------------- + if isfield(MDP,'b') && t > 1 + for k = 1:Np + v = V(t - 1,k); + i = MDP.b{v} > 0; + db = u(k,t - 1)*x(:,t,k)*x(:,t - 1,k)'; + MDP.b{v}(i) = MDP.b{v}(i) + db(i); end - title('Observed states','FontSize',14) - xlabel('trial','FontSize',12) - ylabel('outcome','FontSize',12) - - % expected action - %------------------------------------------------------------------ - subplot(4,2,6) - plot(wn,'k') - title('Expected precision (dopamine)','FontSize',14) - xlabel('updates','FontSize',12) - ylabel('Precision','FontSize',12) - drawnow - - % learned transition matrix - %------------------------------------------------------------------ - subplot(4,1,4) - image(spm_softmax(B0)*64) - title('learned transitions','FontSize',14) - xlabel('states','FontSize',12) - ylabel('states','FontSize',12) - axis square - drawnow - end + + % mapping from hidden states to hidden states - habit: c + %---------------------------------------------------------------------- + if isfield(MDP,'c') && t > 1 + k = Nh; + i = MDP.c > 0; + dc = x(:,t,k)*x(:,t - 1,k)'; + MDP.c(i) = MDP.c(i) + dc(i); + end + end - -% simulated dopamine responses + +% initial hidden states: d %-------------------------------------------------------------------------- -da = gradient(wn) + wn/16; -if PLOT > 0 - subplot(4,2,6), hold on - bar(4*da,'c'), plot(wn,'k'), hold off - spm_axis tight +if isfield(MDP,'d') + i = MDP.d > 0; + MDP.d(i) = MDP.d(i) + X(i,1) - (MDP.d(i) - 1)/16; end - - + +% policies: e +%-------------------------------------------------------------------------- +if isfield(MDP,'e') + MDP.e = MDP.e + u(:,T); +end + +% simulated dopamine (or cholinergic) responses +%-------------------------------------------------------------------------- +if OPTIONS.gamma_s + dn = ch(:,p); +else + dn = 8*gradient(wn) + wn/8; +end + +% Bayesian model averaging of expected hidden states over policies +%-------------------------------------------------------------------------- +Xn = zeros(Ni,Ns,T,T); +for i = 1:T + for k = 1:Np + Xn(:,:,:,i) = Xn(:,:,:,i) + xn(:,:,:,i,k)*u(k,i); + end +end + + % assemble results and place in NDP structure %-------------------------------------------------------------------------- MDP.P = P; % probability of action at time 1,...,T - 1 MDP.Q = x; % conditional expectations over N hidden states -MDP.O = O; % a sparse matrix, encoding outcomes at 1,...,T -MDP.S = S; % a sparse matrix, encoding the states -MDP.U = U; % a sparse matrix, encoding the action -MDP.W = W; % posterior expectations of precision -MDP.c = c; % concentration parameters of transitions -MDP.da = da; % simulated dopamine responses (deconvolved) -MDP.KLx = KLx; % updating as scored with KL (state estimation) -MDP.KLu = KLu; % updating as scored with KL (policy selection) - -return - +MDP.X = X; % Bayesian model averages +MDP.R = u; % conditional expectations over policies +MDP.o = o; % outcomes at 1,...,T +MDP.s = s; % states at 1,...,T +MDP.u = a; % action at 1,...,T +MDP.w = gu; % posterior expectations of precision (policy) +MDP.v = gx; % posterior expectations of precision (states) +MDP.C = Vo; % utility + +MDP.un = un; % simulated neuronal encoding of policies +MDP.xn = Xn; % simulated neuronal encoding of hidden states +MDP.wn = wn; % simulated neuronal encoding of precision +MDP.dn = dn; % simulated dopamine responses (deconvolved) +MDP.rt = rt; % simulated reaction time + +% plot +%========================================================================== +if OPTIONS.plot + if ishandle(OPTIONS.plot) + figure(OPTIONS.plot); clf + else + spm_figure('GetWin','MDP'); clf + end + spm_MDP_VB_trial(MDP) +end +function A = spm_norm(A) +% normalisation of a probability transition matrix (columns) +%-------------------------------------------------------------------------- +A = A*diag(1./sum(A,1)); diff --git a/toolbox/DEM/spm_MDP_VB_LFP.m b/toolbox/DEM/spm_MDP_VB_LFP.m new file mode 100644 index 00000000..e966a49a --- /dev/null +++ b/toolbox/DEM/spm_MDP_VB_LFP.m @@ -0,0 +1,160 @@ +function [u,v] = spm_MDP_VB_LFP(MDP,UNITS,FACTOR) +% auxiliary routine for plotting simulated electrophysiological responses +% FORMAT [u,v] = spm_MDP_VB_LFP(MDP,UNITS,FACTOR) +% +% u - selected unit rate of change of firing (simulated voltage) +% v - selected unit responses {number of trials, number of units} +% +% MDP - structure (see spm_MDP_VB +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_MDP_VB_LFP.m 6657 2015-12-31 17:59:31Z karl $ + + +% defaults +%========================================================================== +try, f = FACTOR; catch, f = 1; end +try, UNITS; catch, UNITS = []; end + + +% dimensions +%-------------------------------------------------------------------------- +Nt = length(MDP); % number of trials +Ne = size(MDP(1).V,1) + 1; % number of epochs +try + Nx = size(MDP(1).B{f},1); % number of states + Nb = size(MDP(1).xn{f},1); % number of time bins per epochs +catch + Nx = size(MDP(1).A,2); % number of states + Nb = size(MDP(1).xn,1); % number of time bins per epochs +end + +% units to plot +%-------------------------------------------------------------------------- +ALL = []; +for i = 1:Ne + for j = 1:Nx + ALL(:,end + 1) = [j;i]; + end +end +if isempty(UNITS) + UNITS = ALL; +end + +% summary statistics +%========================================================================== +for i = 1:Nt + + % all units + %---------------------------------------------------------------------- + try + xn = MDP(i).xn{f}; + catch + xn = MDP(i).xn; + end + for j = 1:size(ALL,2) + for k = 1:Ne + zj{k,j} = xn(:,ALL(1,j),ALL(2,j),k); + xj{k,j} = gradient(zj{k,j}')'; + end + end + z{i,1} = zj; + x{i,1} = xj; + + % selected units + %---------------------------------------------------------------------- + for j = 1:size(UNITS,2) + for k = 1:Ne + vj{k,j} = xn(:,UNITS(1,j),UNITS(2,j),k); + uj{k,j} = gradient(vj{k,j}')'; + end + end + v{i,1} = vj; + u{i,1} = uj; + + % dopamine or changes in precision + %---------------------------------------------------------------------- + dn(:,i) = mean(MDP(i).dn,2); + +end + +if nargout, return, end + +% phase amplitude coupling +%========================================================================== +dt = 1/64; % time bin (seconds) +t = (1:(Nb*Ne*Nt))*dt; % time (seconds) +Hz = 4:32; % frequency range +n = 1/(4*dt); % window length +w = Hz*(dt*n); % cycles per window + +% simulated local field potential +%-------------------------------------------------------------------------- +LFP = spm_cat(x); + +if Nt == 1, subplot(3,2,1), else subplot(4,1,1),end +imagesc(t,1:(Nx*Ne),spm_cat(z)'),title('Unit responses','FontSize',16) +xlabel('time (seconds)','FontSize',12), ylabel('unit','FontSize',12) +grid on, set(gca,'XTick',(1:(Ne*Nt))*Nb*dt) +grid on, set(gca,'YTick',(1:Ne)*Nx) +if Ne*Nt > 32, set(gca,'XTickLabel',[]), end +if Nt == 1, axis square, end + +% time frequency analysis and theta phase +%-------------------------------------------------------------------------- +wft = spm_wft(LFP,w,n); +csd = sum(abs(wft),3); +lfp = sum(LFP,2); +phi = spm_iwft(sum(wft(1,:,:),3),w(1),n); +lfp = 4*lfp/std(lfp) + 16; +phi = 4*phi/std(phi) + 16; + +if Nt == 1, subplot(3,2,3), else subplot(4,1,2),end +imagesc(t,Hz,csd), axis xy, hold on +plot(t,lfp,'w:',t,phi,'w'), hold off +grid on, set(gca,'XTick',(1:(Ne*Nt))*Nb*dt) + +title('Time-frequency response','FontSize',16) +xlabel('time (seconds)','FontSize',12), ylabel('frequency','FontSize',12) +if Nt == 1, axis square, end + +% local field potentials +%========================================================================== +if Nt == 1, subplot(3,2,4), else subplot(4,1,3),end +plot(t,spm_cat(u)), hold off, spm_axis tight, a = axis; +plot(t,spm_cat(x),':'), hold on +plot(t,spm_cat(u)), hold off, axis(a) +grid on, set(gca,'XTick',(1:(Ne*Nt))*Nb*dt), +for i = 2:2:Nt + h = patch(((i - 1) + [0 0 1 1])*Ne*Nb*dt,a([3,4,4,3]),-[1 1 1 1],'w'); + set(h,'LineStyle',':','FaceColor',[1 1 1] - 1/32); +end +title('Local field potentials','FontSize',16) +xlabel('time (seconds)','FontSize',12) +ylabel('Response','FontSize',12) +if Nt == 1, axis square, end + +% firing rates +%========================================================================== +qu = spm_cat(v); +qx = spm_cat(z); +if Nt == 1, subplot(3,2,2) + plot(t,qu), hold on, spm_axis tight, a = axis; + plot(t,qx,':'), hold off + grid on, set(gca,'XTick',(1:(Ne*Nt))*Nb*dt), axis(a) + title('Firing rates','FontSize',16) + xlabel('time (seconds)','FontSize',12) + ylabel('Response','FontSize',12) + axis square +end + +% simulated dopamine responses +%========================================================================== +if Nt == 1, subplot(3,1,3), else subplot(4,1,4),end +bar(spm_vec(dn),1,'k'), title('Phasic dopamine responses','FontSize',16) +xlabel('time (updates)','FontSize',12) +ylabel('change in precision','FontSize',12), spm_axis tight +if Nt == 1, axis square, end + diff --git a/toolbox/DEM/spm_MDP_VB_X.m b/toolbox/DEM/spm_MDP_VB_X.m new file mode 100644 index 00000000..02c09447 --- /dev/null +++ b/toolbox/DEM/spm_MDP_VB_X.m @@ -0,0 +1,694 @@ +function [MDP] = spm_MDP_VB_X(MDP,OPTIONS) +% active inference and learning using variational Bayes (factorised) +% FORMAT [MDP] = spm_MDP_VB_X(MDP,OPTIONS) +% +% MDP.V(T - 1,P,F) - P allowable policies of T moves over F factors +% or +% MDP.U(1,P,F) - P allowable actions at each move +% MDP.T - number of outcomes +% +% MDP.A{G}(O,N1,...,NF) - likelihood of O outcomes given hidden states +% MDP.B{F}(NF,NF,MF) - transitions among hidden under MF control states +% MDP.C{G}(O,T) - prior preferences over O outsomes in modality G +% MDP.D{F}(NF,1) - prior probabilities over initial states +% +% MDP.a{G} - concentration parameters for A +% MDP.b{F} - concentration parameters for B +% MDP.d{F} - concentration parameters for D +% +% optional: +% MDP.s(F,T) - vector of true states - for each hidden factor +% MDP.o(G,T) - vector of outcome - for each outcome modality +% MDP.u(F,T - 1) - vector of action - for each hidden factor +% MDP.w(1,T) - vector of precisions +% +% MDP.alpha - precision – action selection [16] +% MDP.beta - precision over precision (Gamma hyperprior - [1]) +% +% OPTIONS.plot - switch to suppress graphics: (default: [0]) +% +% produces: +% +% MDP.P(M1,...,MF,T) - probability of emitting action over time +% MDP.Q{F}(NF,T,P) - expected hidden states under each policy +% MDP.X{F}(NF,T) - and Bayesian model averages over policies +% MDP.R(P,T) - conditional expectations over policies +% +% MDP.un - simulated neuronal encoding of hidden states +% MDP.xn - simulated neuronal encoding of policies +% MDP.wn - simulated neuronal encoding of precision (tonic) +% MDP.dn - simulated dopamine responses (phasic) +% MDP.rt - simulated reaction times +% +% This routine provides solutions of active inference (minimisation of +% variational free energy) using a generative model based upon a Markov +% decision process. This model and inference scheme is formulated +% in discrete space and time. This means that the generative model (and +% process) are finite state machines or hidden Markov models whose +% dynamics are given by transition probabilities among states and the +% likelihood corresponds to a particular outcome conditioned upon +% hidden states. +% +% This implementation equips agents with the prior beliefs that they will +% maximise expected free energy: expected free energy is the free energy +% of future outcomes under the posterior predictive distribution. This can +% be interpreted in several ways – most intuitively as minimising the KL +% divergence between predicted and preferred outcomes (specified as prior +% beliefs) – while simultaneously minimising ambiguity. +% +% This particular scheme is designed for any allowable policies or control +% sequences specified in MDP.V. Constraints on allowable policies can limit +% the numerics or combinatorics considerably.furthermore, the outcome space +% and hidden states can be defined in terms of factors; corresponding to +% sensory modalities and (functionally) segregated representations, +% respectively. This means, for each factor or subset of hidden states +% there are corresponding control states that determine the transition +% probabilities. +% +% This specification simplifies the generative model, allowing a fairly +% exhaustive model of potential outcomes. In brief, the agent encodes +% beliefs about hidden states in the past and in the future conditioned +% on each policy. The conditional expectations determine the (path +% integral) of free energy that then determines the prior over policies. +% This prior is used to create a predictive distribution over outcomes, +% which specifies the next action. +% +% In addition to state estimation and policy selection, the scheme also +% updates model parameters; including the state transition matrices, +% mapping to outcomes and the initial state. This is useful for learning +% the context. +% +% See also:spm_MDP, which uses multiple future states and a mean field +% approximation for control states – but allows for different actions +% at all times (as in control problems). +% +% See also: spm_MDP_game_KL, which uses a very similar formulation but just +% maximises the KL divergence between the posterior predictive distribution +% over hidden states and those specified by preferences or prior beliefs. +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_MDP_VB_X.m 6672 2016-01-12 12:28:31Z karl $ + + +% deal with a sequence of trials +%========================================================================== + +% options +%-------------------------------------------------------------------------- +try, OPTIONS.plot; catch, OPTIONS.plot = 0; end +try, OPTIONS.gamma_u; catch, OPTIONS.gamma_u = 0; end + +% if there are multiple trials ensure that parameters are updated +%-------------------------------------------------------------------------- +if length(MDP) > 1 + + OPTS = OPTIONS; + OPTS.plot = 0; + for i = 1:length(MDP) + + % update concentration parameters + %------------------------------------------------------------------ + if i > 1 + try, MDP(i).a = OUT(i - 1).a; end + try, MDP(i).b = OUT(i - 1).b; end + try, MDP(i).d = OUT(i - 1).d; end + end + + % solve this trial + %------------------------------------------------------------------ + OUT(i) = spm_MDP_VB_X(MDP(i),OPTS); + + end + MDP = OUT; + + % plot summary statistics - over trials + %---------------------------------------------------------------------- + if OPTIONS.plot + if ishandle(OPTIONS.plot) + figure(OPTIONS.plot); clf + else + spm_figure('GetWin','MDP'); clf + end + spm_MDP_VB_game(MDP) + end + return +end + + +% set up and preliminaries +%========================================================================== +try + V = MDP.U; % allowable actions (1,Np) + T = MDP.T; % number of transitions +catch + V = MDP.V; % allowable policies (T - 1,Np) + T = size(MDP.V,1) + 1; % number of transitions +end + +% numbers of transitions, policies and states +%-------------------------------------------------------------------------- +Nf = numel(MDP.B); % number of hidden state factors +Ng = numel(MDP.A); % number of outcome factors +Np = size(V,2); % number of allowable policies +for f = 1:Nf + Nu(f) = size(MDP.B{f},3); % number of hidden controls + Ns(f) = size(MDP.B{f},1); % number of hidden states +end +for g = 1:Ng + No(g) = size(MDP.A{g},1); % number of outcomes +end +p0 = exp(-8); % smallest probability +q0 = 1/16; % smallest probability + + +% parameters of generative model and policies +%========================================================================== + +% likelihood model (for a partially observed MDP implicit in G) +%-------------------------------------------------------------------------- +for g = 1:Ng + + A{g} = spm_norm(MDP.A{g} + p0); + + % parameters (concentration parameters): A + %---------------------------------------------------------------------- + if isfield(MDP,'a') + qA{g} = spm_psi(MDP.a{g} + q0); + else + qA{g} = log(A{g}); + end + + % entropy + %---------------------------------------------------------------------- + H{g} = spm_ent(qA{g}); + +end + +% transition probabilities (priors) +%-------------------------------------------------------------------------- +for f = 1:Nf + for j = 1:Nu(f) + + % controlable transition probabilities + %------------------------------------------------------------------ + B{f}(:,:,j) = spm_norm(MDP.B{f}(:,:,j) + p0); + + % parameters (concentration parameters): B + %------------------------------------------------------------------ + if isfield(MDP,'b') + sB{f}(:,:,j) = spm_norm((MDP.b{f}(:,:,j) + q0) ); + rB{f}(:,:,j) = spm_norm((MDP.b{f}(:,:,j) + q0)'); + else + sB{f}(:,:,j) = spm_norm(B{f}(:,:,j) ); + rB{f}(:,:,j) = spm_norm(B{f}(:,:,j)'); + end + + end +end + + +% priors over initial hidden states - concentration parameters +%-------------------------------------------------------------------------- +for f = 1:Nf + if isfield(MDP,'d') + qD{f} = spm_psi(MDP.d{f} + q0); + elseif isfield(MDP,'D') + qD{f} = log(spm_norm(MDP.D{f} + p0)); + else + qD{f} = spm_psi(ones(Ns(f),1)); + end +end + + +% prior preferences (log probabilities) : C +%-------------------------------------------------------------------------- +for g = 1:Ng + if isfield(MDP,'C') + Vo{g} = MDP.C{g}; + else + Vo{g} = zeros(No(g),1); + end + + % assume constant preferences, if only final states are specified + %-------------------------------------------------------------------------- + if size(Vo{g},2) ~= T + Vo{g} = Vo{g}(:,end)*ones(1,T); + end + Vo{g} = log(spm_softmax(Vo{g})); +end + +% precision defaults +%-------------------------------------------------------------------------- +try, alpha = MDP.alpha; catch, alpha = 16; end +try, beta = MDP.beta; catch, beta = 1; end + +% initialise +%-------------------------------------------------------------------------- +Ni = 16; % number of VB iterations +rt = zeros(1,T); % reaction times +wn = zeros(T*Ni,1); % simulated DA responses +for f = 1:Nf + + % initialise priors over states + %---------------------------------------------------------------------- + try + s(f,1) = MDP.s(f,1); + catch + s(f,1) = 1; + end + + % initialise posteriors over states + %---------------------------------------------------------------------- + xn{f} = zeros(Ni,Ns(f),T,T,Np) + 1/Ns(f); + x{f} = zeros(Ns(f),T,Np) + 1/Ns(f); + X{f} = zeros(Ns(f),T); + for k = 1:Np + x{f}(:,1,k) = spm_softmax(qD{f}); + end + +end + +% initialise posteriors over polices and action +%-------------------------------------------------------------------------- +P = zeros([Nu,(T - 1)]); +un = zeros(Np,T*Ni); +u = zeros(Np,T - 1); +a = zeros(Nf,T - 1); + + +% initial outcome (index) +%-------------------------------------------------------------------------- +for g = 1:Ng + try + o(g,1) = MDP.o(g,1); + catch + ind = num2cell(s(:,1)); + o(g,1) = find(rand < cumsum(A{g}(:,ind{:})),1); + end +end + +% expected rate parameter +%-------------------------------------------------------------------------- +p = 1:Np; % allowable policies +qbeta = beta; % initialise rate parameters +gu = zeros(1,T) + 1/qbeta; % posterior precision (policy) + +% solve +%========================================================================== +for t = 1:T + + % processing time and reset + %---------------------------------------------------------------------- + tstart = tic; + for f = 1:Nf + x{f} = spm_softmax(log(x{f})/2); + end + + % Variational updates (hidden states) under sequential policies + %====================================================================== + S = size(V,1) + 1; + for i = 1:Ni + px = x; + F = zeros(Np,S); + for f = 1:Nf + for k = p + for j = 1:S + + % evaluate free energy and gradients (v = dFdx) + %====================================================== + + % entropy term + %------------------------------------------------------ + qx = log(x{f}(:,j,k)); + v = qx; + + ind = 1:Nf; + ind(f) = []; + xq = cell(1,(Nf - 1)); + for q = 1:numel(ind) + xq{q} = x{ind(q)}(:,j,k); + end + + % likelihood + %------------------------------------------------------ + if j <= t + for g = 1:Ng + Aq = spm_dot(qA{g},xq,ind + 1); + v = v - Aq(o(g,j),:)'; + end + end + + % emprical prior + %------------------------------------------------------ + if j == 1, v = v - qD{f}; end + if j > 1, v = v - log(sB{f}(:,:,V(j - 1,k,f))*x{f}(:,j - 1,k)); end + if j < S, v = v - log(rB{f}(:,:,V(j ,k,f))*x{f}(:,j + 1,k)); end + + % free energy and belief updating + %------------------------------------------------------ + F(k,j) = F(k,j) - x{f}(:,j,k)'*v; + px{f}(:,j,k) = spm_softmax(qx - v/4); + + % record neuronal activity + %------------------------------------------------------ + xn{f}(i,:,j,t,k) = x{f}(:,j,k); + + end + end + end + + % hidden state updates and convergence + %------------------------------------------------------------------ + x = px; + if i > 1 + if all(sum(F - G,2) < 1/128) + for g = i:Ni + for f = 1:Nf + for k = p + for j = 1:S + xn{f}(g,:,j,t,k) = x{f}(:,j,k); + end + end + end + end + break + end + end + G = F; + end + + % (negative path integral of) free energy of policies (Q) + %====================================================================== + Q = zeros(Np,S); + for k = p + for j = 1:S + xq = cell(1,Nf); + ind = 1:Nf; + for f = 1:Nf + xq{f} = x{f}(:,j,k); + end + for g = 1:Ng + qo = spm_dot(A{g},xq,ind + 1); + Q(k,j) = Q(k,j) + qo'*(Vo{g}(:,j) - log(qo)); + Q(k,j) = Q(k,j) + spm_dot(H{g},xq,ind); + end + end + end + + + % variational updates - policies and precision + %====================================================================== + F = sum(F,2); + Q = sum(Q,2); + if ~isfield(MDP,'U') + p = p((F(p) - max(F(p))) > -3); + end + for i = 1:Ni + + % policy (u) + %------------------------------------------------------------------ + qu = spm_softmax(gu(t)*Q(p) + F(p)); + pu = spm_softmax(gu(t)*Q(p)); + + % precision (gu) with free energy gradients (v = -dF/dw) + %------------------------------------------------------------------ + if OPTIONS.gamma_u + gu(t) = 1/beta; + else + v = qbeta - beta + (qu - pu)'*Q(p); + qbeta = qbeta - v/2; + gu(t) = 1/qbeta; + end + + % simulated dopamine responses (precision at each iteration) + %------------------------------------------------------------------ + n = (t - 1)*Ni + i; + u(p,t) = qu; + wn(n,1) = gu(t); + un(p,n) = qu; + + end + + + % Bayesian model averaging of hidden states (over policies) + %---------------------------------------------------------------------- + for f = 1:Nf + for i = 1:S + X{f}(:,i) = reshape(x{f}(:,i,:),Ns(f),Np)*u(:,t); + end + end + + % processing time + %---------------------------------------------------------------------- + rt(t) = toc(tstart); + + + % action selection and sampling of next state (outcome) + %====================================================================== + if t < T + + % posterior potential for (allowable) actions (for each modality) + %================================================================== + + % unique combinations of actions + %------------------------------------------------------------------ + up = unique(shiftdim(V(t,p,:),1),'rows'); + + % predicted hidden statesat the next time step + %------------------------------------------------------------------ + ind = 1:Nf; + for f = 1:Nf + xp{f} = X{f}(:,t + 1); + end + + % predicted hidden states under each action + %------------------------------------------------------------------ + Pu = zeros(Nu); + for i = 1:size(up,1) + + for f = 1:Nf + xq{f} = B{f}(:,:,up(i,f))*X{f}(:,t); + end + + % accumulate action potential over outcomes + %-------------------------------------------------------------- + for g = 1:Ng + + % predicted outcome + %---------------------------------------------------------- + po = spm_dot(A{g},xp,ind + 1); + qo = spm_dot(A{g},xq,ind + 1); + dP = (log(po) - log(qo))'*qo; + + % augment action potential + %---------------------------------------------------------- + sub = num2cell(up(i,:)); + Pu(sub{:}) = Pu(sub{:}) + dP + 16; + + end + end + + % action selection - a softmax function of action potential + %------------------------------------------------------------------ + sub = repmat({':'},1,Nf); + Pu(:) = spm_softmax(alpha*Pu(:)); + P(sub{:},t) = Pu; + + % next action - sampled from beliefs about control states + %------------------------------------------------------------------ + try + a(:,t) = MDP.u(:,t); + catch + ind = find(rand < cumsum(Pu(:)),1); + a(:,t) = spm_ind2sub(Nu,ind); + end + + % next sampled state - based on the current action + %------------------------------------------------------------------ + try + s(:,t + 1) = MDP.s(:,t + 1); + catch + for f = 1:Nf + s(f,t + 1) = find(rand < cumsum(B{f}(:,s(f,t),a(f,t))),1); + end + end + + % next observed state + %------------------------------------------------------------------ + try + o(:,t + 1) = MDP.o(:,t + 1); + catch + for g = 1:Ng + ind = num2cell(s(:,t + 1)); + o(g,t + 1) = find(rand < cumsum(A{g}(:,ind{:})),1); + end + end + + % next expected precision + %------------------------------------------------------------------ + gu(1,t + 1) = gu(t); + + % update policy if necessary + %------------------------------------------------------------------ + if isfield(MDP,'U') && t < (T - 1) + for f = 1:Nf + V(t,:,f) = a(f,t); + end + for j = 1:size(MDP.U,1) + if (t + j) < T + V(t + j,:,:) = MDP.U(j,:,:); + end + end + + % and reinitialise expectations about hidden states + %-------------------------------------------------------------- + j = 1:t; + for f = 1:Nf + for k = 1:Np + x{f}(:,j,k) = x{f}(:,j,a(f,t)); + end + end + + end + end +end + +% learning +%========================================================================== +for t = 1:T + + % mapping from hidden states to outcomes: a + %---------------------------------------------------------------------- + if isfield(MDP,'a') + for g = 1:Ng + da = sparse(o(g,t),1,1,No(g),1); + for f = 1:Nf + da = spm_cross(da,X{f}(:,t)); + end + MDP.a{g} = MDP.a{g} + da.*(MDP.a{g} > 0); + end + end + + % mapping from hidden states to hidden states: b(u) + %---------------------------------------------------------------------- + if isfield(MDP,'b') && t > 1 + for f = 1:Nf + for k = 1:Np + v = V(t - 1,k,f); + db = u(k,t - 1)*x{f}(:,t,k)*x{f}(:,t - 1,k)'; + MDP.b{f}(:,:,v) = MDP.b{f}(:,:,v) + db.*(MDP.b{f}(:,:,v) > 0); + end + end + end + +end + +% initial hidden states: d +%-------------------------------------------------------------------------- +if isfield(MDP,'d') + for f = 1:Nf + i = MDP.d{f} > 0; + MDP.d{f}(i) = MDP.d{f}(i) + X{f}(i,1) - (MDP.d{f}(i) - 1)/16; + end +end + +% simulated dopamine (or cholinergic) responses +%-------------------------------------------------------------------------- +dn = 8*gradient(wn) + wn/8; + +% Bayesian model averaging of expected hidden states over policies +%-------------------------------------------------------------------------- +for f = 1:Nf + Xn{f} = zeros(Ni,Ns(f),T,T); + for i = 1:T + for k = 1:Np + Xn{f}(:,:,:,i) = Xn{f}(:,:,:,i) + xn{f}(:,:,:,i,k)*u(k,i); + end + end +end + + +% assemble results and place in NDP structure +%-------------------------------------------------------------------------- +MDP.P = P; % probability of action at time 1,...,T - 1 +MDP.Q = x; % conditional expectations over N hidden states +MDP.X = X; % Bayesian model averages +MDP.R = u; % conditional expectations over policies +MDP.V = V; % policies +MDP.o = o; % outcomes at 1,...,T +MDP.s = s; % states at 1,...,T +MDP.u = a; % action at 1,...,T +MDP.w = gu; % posterior expectations of precision (policy) +MDP.C = Vo; % utility + +MDP.un = un; % simulated neuronal encoding of policies +MDP.xn = Xn; % simulated neuronal encoding of hidden states +MDP.wn = wn; % simulated neuronal encoding of precision +MDP.dn = dn; % simulated dopamine responses (deconvolved) +MDP.rt = rt; % simulated reaction time + +% plot +%========================================================================== +if OPTIONS.plot + if ishandle(OPTIONS.plot) + figure(OPTIONS.plot); clf + else + spm_figure('GetWin','MDP'); clf + end + spm_MDP_VB_trial(MDP) +end + + +function A = spm_norm(A) +% normalisation of a probability transition matrix (columns) +%-------------------------------------------------------------------------- +for i = 1:size(A,2) + for j = 1:size(A,3) + for k = 1:size(A,4) + for l = 1:size(A,5) + A(:,i,j,k,l) = A(:,i,j,k,l)/sum(A(:,i,j,k,l),1); + end + end + end +end + +function A = spm_psi(A) +% normalisation of a probability transition rate matrix (columns) +%-------------------------------------------------------------------------- +for i = 1:size(A,2) + for j = 1:size(A,3) + for k = 1:size(A,4) + for l = 1:size(A,5) + A(:,i,j,k,l) = psi(A(:,i,j,k,l)) - psi(sum(A(:,i,j,k,l))); + end + end + end +end + +function H = spm_ent(A) +% normalisation of a probability transition matrix (columns) +%-------------------------------------------------------------------------- +for i = 1:size(A,2) + for j = 1:size(A,3) + for k = 1:size(A,4) + for l = 1:size(A,5) + H(i,j,k,l) = spm_softmax(A(:,i,j,k,l))'*A(:,i,j,k,l); + end + end + end +end + +function sub = spm_ind2sub(siz,ndx) +% subscripts from linear index +%-------------------------------------------------------------------------- +n = numel(siz); +k = [1 cumprod(siz(1:end-1))]; +for i = n:-1:1, + vi = rem(ndx - 1,k(i)) + 1; + vj = (ndx - vi)/k(i) + 1; + sub(i,1) = vj; + ndx = vi; +end + diff --git a/toolbox/DEM/spm_MDP_VB_game.m b/toolbox/DEM/spm_MDP_VB_game.m new file mode 100644 index 00000000..2557748c --- /dev/null +++ b/toolbox/DEM/spm_MDP_VB_game.m @@ -0,0 +1,218 @@ +function Q = spm_MDP_VB_game(MDP) +% auxiliary plotting routine for spm_MDP_VB - multiple trials +% FORMAT Q = spm_MDP_VB_game(MDP) +% +% MDP.P(M,T) - probability of emitting action 1,...,M at time 1,...,T +% MDP.Q(N,T) - an array of conditional (posterior) expectations over +% N hidden states and time 1,...,T +% MDP.X - and Bayesian model averages over policies +% MDP.R - conditional expectations over policies +% MDP.O(O,T) - a sparse matrix encoding outcomes at time 1,...,T +% MDP.S(N,T) - a sparse matrix encoding states at time 1,...,T +% MDP.U(M,T) - a sparse matrix encoding action at time 1,...,T +% MDP.W(1,T) - posterior expectations of precision +% +% MDP.un = un - simulated neuronal encoding of hidden states +% MDP.xn = Xn - simulated neuronal encoding of policies +% MDP.wn = wn - simulated neuronal encoding of precision +% MDP.da = dn - simulated dopamine responses (deconvolved) +% MDP.rt = rt - simulated dopamine responses (deconvolved) +% +% returns summary of performance: +% +% Q.X = x - expected hidden states +% Q.R = u - final policy expectations +% Q.S = s - initial hidden states +% Q.O = o - final outcomes +% Q.p = p - performance +% Q.q = q - reaction times +% +% please see spm_MDP_VB +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_MDP_VB_game.m 6657 2015-12-31 17:59:31Z karl $ + +% numbers of transitions, policies and states +%-------------------------------------------------------------------------- +if iscell(MDP(1).X) + Nf = numel(MDP(1).B); % number of hidden state factors + Ng = numel(MDP(1).A); % number of outcome factors +else + Nf = 1; + Ng = 1; +end + +% graphics +%========================================================================== +Nt = length(MDP); % number of trials +Ne = size(MDP(1).V,1) + 1; % number of epochs per trial +Np = size(MDP(1).V,2) + 1; % number of policies +for i = 1:Nt + + % assemble expectations of hidden states and outcomes + %---------------------------------------------------------------------- + for j = 1:Ne + for k = 1:Ne + for f = 1:Nf + try + x{f}{i,1}{k,j} = gradient(MDP(i).xn{f}(:,:,j,k)')'; + catch + x{f}{i,1}{k,j} = gradient(MDP(i).xn(:,:,j,k)')'; + end + end + end + end + s(:,i) = MDP(i).s(:,1); + o(:,i) = MDP(i).o(:,end); + u(:,i) = MDP(i).R(:,end); + w(:,i) = mean(MDP(i).dn,2); + + + % assemble context learning + %---------------------------------------------------------------------- + for f = 1:Nf + try + try + D = MDP(i).d{f}; + catch + D = MDP(i).D{f}; + end + catch + try + D = MDP(i).d; + catch + D = MDP(i).D; + end + end + d{f}(:,i) = D/sum(D); + end + + % assemble performance + %---------------------------------------------------------------------- + p(i) = 0; + for g = 1:Ng + try + U = spm_softmax(MDP(i).C{g}); + catch + U = spm_softmax(MDP(i).C); + end + for t = 1:Ne + p(i) = p(i) + log(U(MDP(i).o(g,t),t))/Ne; + end + end + q(i) = sum(MDP(i).rt(2:end)); + +end + +% assemble output structure if required +%-------------------------------------------------------------------------- +if nargout + Q.X = x; % expected hidden states + Q.R = u; % final policy expectations + Q.S = s; % inital hidden states + Q.O = o; % final outcomes + Q.p = p; % performance + Q.q = q; % reaction times + return +end + + +% Initial states and expected policies (habit in red) +%-------------------------------------------------------------------------- +col = {'r.','b.','g.','c.','m.','k.'}; +t = 1:Nt; +subplot(6,1,1) +if Nt < 64 + MarkerSize = 24; +else + MarkerSize = 16; +end +image(64*(1 - u)), hold on +for f = 1:Nf + for i = 1:max(s(f,:)) + j = find(s(f,:) == i); + plot(t(j),j - j + f,col{rem(i - 1,6)+ 1},'MarkerSize',MarkerSize) + end +end +try + plot(Np*(1 - u(Np,:)),'r') +end +try + E = spm_softmax(spm_cat({MDP.e})); + plot(Np*(1 - E(end,:)),'r:') +end +title('Initial state and policy selection') +xlabel('Trial'),ylabel('Policy'), hold off + + +% Performance +%-------------------------------------------------------------------------- +q = q - mean(q); +q = q/std(q); +subplot(6,1,2), bar(p,'k'), hold on +plot(q,'.c','MarkerSize',16), hold on +plot(q,':c') +for g = 1:Ng + for i = 1:max(o(g,:)) + j = find(o(g,:) == i); + plot(t(j),j - j + 3 + g,col{rem(i - 1,6)+ 1},'MarkerSize',MarkerSize) + end +end +title('Final outcome, performance and reaction times') +ylabel('Expected utility'), spm_axis tight, hold off + +% Initial states (context) +%-------------------------------------------------------------------------- +subplot(6,1,3) +col = {'r','b','g','c','m','k'}; +for f = 1:Nf + if Nf > 1 + plot(spm_cat(x{f}),col{f}), hold on + else + plot(spm_cat(x{f})) + end +end +title('State estimation (ERPs)') +ylabel('Response'), spm_axis tight, hold off + +% Precision (dopamine) +%-------------------------------------------------------------------------- +subplot(6,1,4) +w = spm_vec(w); +if Nt > 8 + fill([1 1:length(w) length(w)],[0; w.*(w > 0); 0],'k'), hold on + fill([1 1:length(w) length(w)],[0; w.*(w < 0); 0],'k'), hold off +else + bar(w,1.1,'k') +end +title('Precision (dopamine)') +ylabel('Precision','FontSize',12), spm_axis tight + +% learning - D +%-------------------------------------------------------------------------- +for f = 1:Nf + subplot(6*Nf,1,Nf*4 + f), image(64*(1 - d{f})) + if f < 2 + title('Context Learning') + end + try + ylabel(MDP(1).Bname{f}) + end +end +if isfield(MDP(1),'c') + title('Learning (C and D)') +else + return +end + +% Habit learning +%-------------------------------------------------------------------------- +k = round(linspace(1,Nt,6)); +for j = 1:length(k) + h = MDP(k(j)).c; + h = h*diag(1./sum(h)); + subplot(6,6,30 + j), image(64*(1 - h)) + axis image +end diff --git a/toolbox/DEM/spm_MDP_VB_trial.m b/toolbox/DEM/spm_MDP_VB_trial.m new file mode 100644 index 00000000..14ecf1b8 --- /dev/null +++ b/toolbox/DEM/spm_MDP_VB_trial.m @@ -0,0 +1,151 @@ +function spm_MDP_VB_trial(MDP) +% auxiliary plotting routine for spm_MDP_VB - single trial +% FORMAT spm_MDP_VB_trial(MDP) +% +% MDP.P(M,T) - probability of emitting action 1,...,M at time 1,...,T +% MDP.Q(N,T) - an array of conditional (posterior) expectations over +% N hidden states and time 1,...,T +% MDP.X - and Bayesian model averages over policies +% MDP.R - conditional expectations over policies +% MDP.o - outcomes at time 1,...,T +% MDP.s - states at time 1,...,T +% MDP.u - action at time 1,...,T +% +% MDP.un = un; - simulated neuronal encoding of hidden states +% MDP.xn = Xn; - simulated neuronal encoding of policies +% MDP.wn = wn; - simulated neuronal encoding of precision +% MDP.da = dn; - simulated dopamine responses (deconvolved) +% MDP.rt = rt; - simulated reaction times +% +% please see spm_MDP_VB +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_MDP_VB_trial.m 6672 2016-01-12 12:28:31Z karl $ + +% graphics +%========================================================================== + +% numbers of transitions, policies and states +%-------------------------------------------------------------------------- +if iscell(MDP.X) + Nf = numel(MDP.B); % number of hidden state factors + Ng = numel(MDP.A); % number of outcome factors + X = MDP.X; + C = MDP.C; + for f = 1:Nf + Nu(f) = size(MDP.B{f},3) > 1; + end +else + Nf = 1; + Ng = 1; + Nu = 1; + X = {MDP.X}; + C = {MDP.C}; +end + + + +% posterior beliefs about hidden states +%-------------------------------------------------------------------------- +for f = 1:Nf + subplot(3*Nf,2,(f - 1)*2 + 1) + image(64*(1 - X{f})), hold on + if size(X{f},1) > 128 + spm_spy(X{f},16,1) + end + plot(MDP.s(f,:),'.c','MarkerSize',16), hold off + try + title(sprintf('Hidden states - %s',MDP.Bname{f})); + catch + if f < 2, title('Hidden states'); end + end + if f == Nf, xlabel('time'), end + ylabel('hidden state') +end + +% posterior beliefs about control states +%-------------------------------------------------------------------------- +Nu = find(Nu); +Np = length(Nu); +for f = 1:Np + subplot(3*Np,2,f*2) + P = MDP.P; + if Nf > 1 + ind = 1:Nf; + for dim = 1:Nf + if dim ~= ind(Nu(f)); + P = sum(P,dim); + end + end + P = squeeze(P); + end + + % display + %---------------------------------------------------------------------- + image(64*(1 - P)), hold on + plot(MDP.u(Nu(f),:),'.c','MarkerSize',16), hold off + try + title(sprintf('Inferred and selected action - %s',MDP.Bname{Nu(f)})); + catch + if f < 2, title('Inferred and selected action'); end + end + if f == Np, xlabel('time'), end + ylabel('action') +end + +% policies +%-------------------------------------------------------------------------- +for f = 1:Np + subplot(3*Np,2,(Np + f - 1)*2 + 1) + imagesc(MDP.V(:,:,Nu(f))') + try + title(sprintf('Allowable policies - %s',MDP.Bname{Nu(f)})); + catch + if f < 2, title('Allowable policies'); end + end + if Np == 1, xlabel('time'), end + ylabel('policy') +end + +% expectations over policies +%-------------------------------------------------------------------------- +subplot(3,2,4) +image(64*(1 - MDP.un)) +title('Posterior probability','FontSize',14) +ylabel('policy','FontSize',12) +xlabel('updates','FontSize',12) + +% sample (observation) and preferences +%-------------------------------------------------------------------------- +for g = 1:Ng + subplot(3*Ng,2,(2*Ng + g - 1)*2 + 1) + if size(C{g},1) > 128 + spm_spy(C{g},16,1), hold on + else + imagesc(1 - C{g}), hold on + end + plot(MDP.o(g,:),'.c','MarkerSize',16), hold off + try + title(sprintf('Outcomes and preferences - %s',MDP.Aname{g})); + catch + if f < 2, title('Outcomes and preferences'); end + end + if g == Ng, xlabel('time'), end + ylabel('outcome') +end + +% expected precision +%-------------------------------------------------------------------------- +subplot(3,2,6), hold on +if size(MDP.dn,2) > 1 + plot(MDP.dn,'r:'), plot(MDP.wn,'k'), hold off +else + bar(MDP.dn,1.1,'k'), plot(MDP.wn,'k'), hold off +end +title('Expected precision (dopamine)','FontSize',14) +xlabel('updates','FontSize',12) +ylabel('precision','FontSize',12) +spm_axis tight +drawnow diff --git a/toolbox/DEM/spm_MDP_check.m b/toolbox/DEM/spm_MDP_check.m new file mode 100644 index 00000000..50042ed2 --- /dev/null +++ b/toolbox/DEM/spm_MDP_check.m @@ -0,0 +1,196 @@ +function [MDP] = spm_MDP_check(MDP) +% MDP structure checking +% FORMAT [MDP] = spm_MDP_check(MDP) +% +% MDP.V(T - 1,P,F) - P allowable policies of T moves over F factors +% or +% MDP.U(1,P,F) - P allowable actions at each move +% MDP.T - number of outcomes +% +% MDP.A{G}(O,N1,...,NF) - likelihood of O outcomes given hidden states +% MDP.B{F}(NF,NF,MF) - transitions among hidden under MF control states +% MDP.C{G}(O,T) - prior preferences over O outcomes in modality G +% MDP.D{F}(NF,1) - prior probabilities over initial states +% +% MDP.a{G} - concentration parameters for A +% MDP.b{F} - concentration parameters for B +% MDP.d{F} - concentration parameters for D +% +% optional: +% MDP.s(F,T) - vector of true states - for each hidden factor +% MDP.o(G,T) - vector of outcome - for each outcome modality +% MDP.u(F,T - 1) - vector of action - for each hidden factor +% MDP.w(1,T) - vector of precisions +% +% if C or D are not specified, they will be set to default values (of no +% preferences and uniform priors over initial steps). If there are no +% policies, it will be assumed that I = 1 and all policies (for each +% marginal hidden state) are allowed. +%__________________________________________________________________________ +% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_MDP_check.m 6672 2016-01-12 12:28:31Z karl $ + + +% deal with a sequence of trials +%========================================================================== + +% if there are multiple structures check each separately +%-------------------------------------------------------------------------- +if length(MDP) > 1 + for i = 1:length(MDP) + MDP(i) = spm_MDP_check(MDP(i)); + end + return +end + +% check dimensions and orders +%========================================================================== + +% numbers of transitions, policies and states +%-------------------------------------------------------------------------- +Nf = numel(MDP.B); % number of hidden state factors +Ng = numel(MDP.A); % number of outcome factors +for f = 1:Nf + Nu(f) = size(MDP.B{f},3); % number of hidden controls + Ns(f) = size(MDP.B{f},1); % number of hidden states + MDP.B{f} = double(MDP.B{f}); +end +for g = 1:Ng + No(g) = size(MDP.A{g},1); % number of outcomes + MDP.A{g} = double(MDP.A{g}); +end + +% check policy specification +%-------------------------------------------------------------------------- +try + V = MDP.U; % allowable actions (1,Np) +catch + try + V = MDP.V; % allowable policies (T - 1,Np) + catch + + % components allowable policies from allowable actions + %------------------------------------------------------------------ + for f = 1:Nf + u = 1; + for i = 1:Nf + if i == f + u = kron(1:Nu(i),u); + else + u = kron(ones(1,Nu(i)),u); + end + end + U(1,:,f) = u; + end + MDP.U = U; + V = MDP.U; + end +end + +% check policy specification +%-------------------------------------------------------------------------- +if Nf ~= size(V,3); + error('please ensure V(:,:,1:Nf) is consistent with MDP.B{1:Nf}') +end + +% check preferences +%-------------------------------------------------------------------------- +if ~isfield(MDP,'C') + for g = 1:Ng + MDP.C{g} = zeros(No(g),1); + end +end +for g = 1:Ng + if No(g) ~= size(MDP.C{g},1); + error(['please ensure A{' num2str(g) '} and C{' num2str(g) '} are consistent']) + end +end + +% check iinitial states +%-------------------------------------------------------------------------- +if ~isfield(MDP,'D') + for f = 1:Nf + MDP.D{f} = ones(Ns(f),1); + end +end +if Nf ~= numel(MDP.D); + error('please ensure V(:,:,1:Nf) is consistent with MDP.D{1:Nf}') +end + + +% check iinitial states and internal consistency +%-------------------------------------------------------------------------- +if Nf ~= numel(MDP.D); + error('please ensure V(:,:,1:Nf) is consistent with MDP.D{1:Nf}') +end +for f = 1:Nf + if Ns(f) ~= size(MDP.D{f},1); + error(['please ensure B{' num2str(f) '} and D{' num2str(f) '} are consistent']) + end + if Nu(f) < max(spm_vec(V(:,:,f))); + error(['please check V(:,:,' num2str(f) ') or U(:,:,' num2str(f) ')']) + end + for g = 1:Ng + Na = size(MDP.A{g}); + if ~all(Na(2:end) == Ns); + error(['please ensure A{' num2str(g) '} and D{' num2str(f) '} are consistent']) + end + end +end + +% check probability matrices are properly specified +%-------------------------------------------------------------------------- +for f = 1:Nf + if ~all(spm_vec(any(MDP.B{f},1))) + error(['please check B{' num2str(f) '} for missing entries']) + end +end +for g = 1:Ng + if ~all(spm_vec(any(MDP.A{g},1))) + error(['please check A{' num2str(g) '} for missing entries']) + end +end + +% check initial states if specified +%-------------------------------------------------------------------------- +if isfield(MDP,'s') + if size(MDP.s,1) ~= Nf + error('please specify an initial state MDP.s for each factor') + end + if any(max(MDP.s,[],2) > Ns(:)) + error('please ensure initial states MDP.s are consistent with MDP.B') + end +else + MDP.s = ones(Nf,1); +end + +% check outcomes if specified +%-------------------------------------------------------------------------- +if isfield(MDP,'o') + if size(MDP.o,1) ~= Ng + error('please specify an outcomes MDP.o for each modality') + end + if any(max(MDP.o,[],2) > No(:)) + error('please ensure outcomes MDP.o are consistent with MDP.A') + end +else + MDP.s = ones(Nf,1); +end + +% check names is specified +%-------------------------------------------------------------------------- +if isfield(MDP,'Aname') + if numel(MDP.Aname) ~= Ng + error('please specify an MDP.Aname for each modality') + end +end +if isfield(MDP,'Bname') + if numel(MDP.Bname) ~= Nf + error('please specify an MDP.Bname for each factor') + end +end + + + diff --git a/toolbox/DEM/spm_MDP_game.m b/toolbox/DEM/spm_MDP_game.m index 43400d95..9d980bfd 100644 --- a/toolbox/DEM/spm_MDP_game.m +++ b/toolbox/DEM/spm_MDP_game.m @@ -24,7 +24,7 @@ % (default: MDP.G{T,M} = MDP.G{M} = MDP.B{M}) % % MDP.plot - switch to suppress graphics: (default: [0]) -% MDP.alpha - upper bound on precision (Gamma hyperprior – shape [8]) +% MDP.alpha - upper bound on precision (Gamma hyperprior - shape [8]) % MDP.beta - precision over precision (Gamma hyperprior - rate [1]) % MDP.gamma - initial precision % MDP.lamba - precision update rate @@ -59,9 +59,9 @@ % This implementation equips agents with the prior beliefs that they will % maximise expected free energy: expected free energy is the free energy % of future outcomes under the posterior predictive distribution. This can -% be interpreted in several ways – most intuitively as minimising the KL +% be interpreted in several ways - most intuitively as minimising the KL % divergence between predicted and preferred outcomes (specified as prior -% beliefs) – while simultaneously minimising the (predicted) entropy of +% beliefs) - while simultaneously minimising the (predicted) entropy of % outcomes conditioned upon hidden states. Expected free energy therefore % combines KL optimality based upon preferences or utility functions with % epistemic value or information gain. @@ -70,9 +70,9 @@ % sequences specified in MDP.V. Constraints on allowable policies can limit % the numerics or combinatorics considerable. For example, situations in % which one action can be selected at one time can be reduced to T polices -% – with one (shift) control being emitted at all possible time points. +% - with one (shift) control being emitted at all possible time points. % This specification of polices simplifies the generative model, allowing a -% fairly exhaustive model of potential outcomes – eschewing a mean field +% fairly exhaustive model of potential outcomes - eschewing a mean field % approximation over successive control states. In brief, the agent simply % represents the current state and states in the immediate and distant % future. @@ -92,17 +92,17 @@ % into the transition probabilities G. % % See also:spm_MDP, which uses multiple future states and a mean field -% approximation for control states – but allows for different actions +% approximation for control states - but allows for different actions % at all times (as in control problems). % % See also: spm_MDP_game_KL, which uses a very similar formulation but just % maximises the KL divergence between the posterior predictive distribution % over hidden states and those specified by preferences or prior beliefs. %__________________________________________________________________________ -% Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2013-2015 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_MDP_game.m 6450 2015-05-24 14:28:03Z karl $ +% $Id: spm_MDP_game.m 6656 2015-12-24 16:49:52Z guillaume $ % set up and preliminaries %========================================================================== @@ -128,7 +128,7 @@ % set up figure if necessary %-------------------------------------------------------------------------- -if PLOT +if ~isequal(PLOT,0) if ishandle(PLOT) figure(PLOT); clf PLOT = 2; @@ -181,7 +181,7 @@ C = A*C; end - % asume no preferences if only final outceoms are specifed + % asume no preferences if only final outcomes are specifed %---------------------------------------------------------------------- if size(C,2) ~= T C = C(:,end)*ones(1,T); @@ -233,7 +233,7 @@ % initial states and outcomes %-------------------------------------------------------------------------- -[p q] = max(A*MDP.S(:,1)); % initial outcome (index) +[p,q] = max(A*MDP.S(:,1)); % initial outcome (index) s = find( MDP.S(:,1)); % initial state (index) o = sparse(1,1,q,1,T); % observations (index) S = sparse(s,1,1,Ns,T); % states sampled (1 in K vector) diff --git a/toolbox/DEM/spm_MDP_urn.m b/toolbox/DEM/spm_MDP_urn.m index ab8a7575..08e5e18c 100644 --- a/toolbox/DEM/spm_MDP_urn.m +++ b/toolbox/DEM/spm_MDP_urn.m @@ -3,7 +3,7 @@ %__________________________________________________________________________ % % This demonstration uses the Urn or Beads Task to illustrate how choice -% behaviour can be simulated using active inference – in the context of +% behaviour can be simulated using active inference - in the context of % Markov decision processes. In the urn task, a succession of draws % from one of two urns are made and the agent has to decide whether the % balls are being drawn from an urn with predominantly red or green balls. @@ -19,7 +19,7 @@ % % This routine first provides an illustration of a game in which a decision % is delayed until the last draw to look at inferences during successive -% draws – with a special focus on precision. The illustration here shows +% draws - with a special focus on precision. The illustration here shows % a decrease in precision when an unexpected (green ball) is drawn during a % sequence of red balls. % @@ -33,7 +33,7 @@ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_MDP_urn.m 6064 2014-06-23 09:39:46Z karl $ +% $Id: spm_MDP_urn.m 6592 2015-11-06 16:20:48Z guillaume $ % set up and preliminaries %========================================================================== @@ -51,7 +51,7 @@ D = spm_vec(D); -% likelihood – everything is seen apart from the hidden states of the urn +% likelihood - everything is seen apart from the hidden states of the urn %-------------------------------------------------------------------------- A = kron([1 1],eye(3*T*T)); diff --git a/toolbox/FieldMap/FieldMap_Run.m b/toolbox/FieldMap/FieldMap_Run.m index e69f65e0..3a7e3008 100644 --- a/toolbox/FieldMap/FieldMap_Run.m +++ b/toolbox/FieldMap/FieldMap_Run.m @@ -28,10 +28,10 @@ % shortimag - name of short imaginary image for real/imaginary job % longimag - name of long imaginary image for real/imaginary job %__________________________________________________________________________ -% Copyright (C) 2007-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2007-2015 Wellcome Trust Centre for Neuroimaging % Chloe Hutton & Jesper Andersson -% $Id: FieldMap_Run.m 6258 2014-11-07 18:15:40Z guillaume $ +% $Id: FieldMap_Run.m 6656 2015-12-24 16:49:52Z guillaume $ %-------------------------------------------------------------------------- @@ -133,12 +133,13 @@ else pm_defs.sessname = 'session'; end + %-------------------------------------------------------------------------- % Call FieldMap_create %-------------------------------------------------------------------------- [VDM, IPcell] = FieldMap_create(fm_imgs,epi_img,pm_defs); -for sessnum=1:max([1 nsessions]); +for sessnum=1:max([1 nsessions]) IP = IPcell{sessnum}; diff --git a/toolbox/FieldMap/FieldMap_create.m b/toolbox/FieldMap/FieldMap_create.m index 152a8099..fb38ad1d 100644 --- a/toolbox/FieldMap/FieldMap_create.m +++ b/toolbox/FieldMap/FieldMap_create.m @@ -15,10 +15,10 @@ % For an introduction to the theoretcial and practical principles behind % the toolbox, see principles.man. %__________________________________________________________________________ -% Copyright (C) 2006-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2006-2015 Wellcome Trust Centre for Neuroimaging % Chloe Hutton -% $Id: FieldMap_create.m 5962 2014-04-17 12:47:43Z spm $ +% $Id: FieldMap_create.m 6504 2015-07-22 13:42:43Z guillaume $ if nargin < 3 error('field map images, epi image and defaults'); @@ -180,6 +180,7 @@ IPcell{1}=IP; elseif nsessions==1 IP.epiP = spm_vol(epi_img{1}(1,:)); + if numel(IP.epiP) > 1, IP.epiP = IP.epiP(1); end % 4D if isfield(pm_defs, 'match_vdm') if pm_defs.match_vdm IP.vdmP = FieldMap('MatchVDM',IP); @@ -217,6 +218,7 @@ IP.vdmP=orig_vdm; % Make sure we start with original for each session Ovdm=IP.vdmP; IP.epiP = spm_vol(epi_img{sessnum}); + if numel(IP.epiP) > 1, IP.epiP = IP.epiP(1); end % 4D if isfield(pm_defs, 'match_vdm') if pm_defs.match_vdm msg=sprintf('\nMatching session %d...\n',sessnum); diff --git a/toolbox/FieldMap/pm_diff.m b/toolbox/FieldMap/pm_diff.m index 7d84dedd..0aaf93c4 100644 --- a/toolbox/FieldMap/pm_diff.m +++ b/toolbox/FieldMap/pm_diff.m @@ -1,37 +1,37 @@ -function V = pm_diff(V,dir) +function D = pm_diff(V,dir) % Calculate derivative in one direction of volume (matrix or memory mapped) +% FORMAT D = pm_diff(V,dir) +% V - 3D array, or filestruct returned from spm_vol +% dir - direction (1, 2 or 3 for x, y or z respectively) % -% FORMAT: V = pm_diff(V,dir) -% -% Input: -% V : 3D array, or filestruct returned from spm_vol. -% dir : Direction (1, 2 or 3 for x, y or z respectively). -% Output: -% V : 3D array of derivatives. -% +% D - 3D array of derivatives %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Chloe Hutton -% $Id: pm_diff.m 4842 2012-08-15 18:02:30Z guillaume $ +% $Id: pm_diff.m 6656 2015-12-24 16:49:52Z guillaume $ -if ischar(V) && exist(V) == 2 +if ischar(V) V = spm_vol(V); - dim = V.dim(1:3); -elseif isstruct(V) && isfield(V,'dim') - dim = V.dim(1:3); +end +if isstruct(V) + dim = V.dim; else dim = size(V); end +dim = [dim 1 1 1]; -hold = 1; +hold = 1; [x,y,z] = ndgrid(1:dim(1),1:dim(2),1:dim(3)); [X,dX,dY,dZ] = spm_sample_vol(V,x,y,z,hold); -if dir == 1 - V = reshape(dX,dim(1),dim(2),dim(3)); -elseif dir == 2 - V = reshape(dY,dim(1),dim(2),dim(3)); -elseif dir == 3 - V = reshape(dZ,dim(1),dim(2),dim(3)); +switch dir + case 1 + D = reshape(dX,dim(1),dim(2),dim(3)); + case 2 + D = reshape(dY,dim(1),dim(2),dim(3)); + case 3 + D = reshape(dZ,dim(1),dim(2),dim(3)); + otherwise + error('Unknown direction.'); end diff --git a/toolbox/FieldMap/pm_pad.m b/toolbox/FieldMap/pm_pad.m index 507356e7..c4d66eaf 100644 --- a/toolbox/FieldMap/pm_pad.m +++ b/toolbox/FieldMap/pm_pad.m @@ -9,7 +9,7 @@ % and some not. % wmap : Wrap-map, where a non-zero value indicates corresponding % phase-value in pm has been unwrapped. -% kernel : kernel used to generate a weighted average of surrounding +% kernel : kernel used to generate a weighted average of surrounding % voxels. % % Output: @@ -17,12 +17,10 @@ % phase-values have now been replaced. % wmap : Same as wmap in, but where values that was replaced % by weighted average in pm have now been set. -%__________________________________________________________________ -% Jesper Andersson 30/9-03 -%_______________________________________________________________________ -% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging +%__________________________________________________________________________ +% Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging % Jesper Andersson -% $Id: pm_pad.m 4842 2012-08-15 18:02:30Z guillaume $ +% $Id: pm_pad.m 6501 2015-07-17 14:32:09Z spm $ error('mex-function pm_pad.c not compiled'); diff --git a/toolbox/FieldMap/tbx_cfg_fieldmap.m b/toolbox/FieldMap/tbx_cfg_fieldmap.m index a351c3fb..e0121485 100644 --- a/toolbox/FieldMap/tbx_cfg_fieldmap.m +++ b/toolbox/FieldMap/tbx_cfg_fieldmap.m @@ -1,9 +1,9 @@ function fieldmap = tbx_cfg_fieldmap % MATLABBATCH Configuration file for toolbox 'FieldMap' %__________________________________________________________________________ -% Copyright (C) 2008-2014 Wellcome Trust Centre for Neuroimaging +% Copyright (C) 2008-2015 Wellcome Trust Centre for Neuroimaging -% $Id: tbx_cfg_fieldmap.m 6458 2015-05-27 16:22:09Z spm $ +% $Id: tbx_cfg_fieldmap.m 6501 2015-07-17 14:32:09Z spm $ addpath(fullfile(spm('dir'),'toolbox','FieldMap')); @@ -44,7 +44,7 @@ blipdir = cfg_menu; blipdir.tag = 'blipdir'; blipdir.name = 'Blip direction'; -blipdir.help = {'Enter the blip direction. This is the polarity of the phase-encode blips describing the direction in which k-space is traversed along the y-axis during EPI acquisition with respect to the coordinate system used in SPM. In this coordinate system, the phase encode direction corresponds with the y-direction and is deï¬ned as positive from the posterior to the anterior of the head.'}; +blipdir.help = {'Enter the blip direction. This is the polarity of the phase-encode blips describing the direction in which k-space is traversed along the y-axis during EPI acquisition with respect to the coordinate system used in SPM. In this coordinate system, the phase encode direction corresponds with the y-direction and is defined as positive from the posterior to the anterior of the head.'}; blipdir.labels = {'-1' '1'}; blipdir.values = {-1 1}; @@ -59,7 +59,7 @@ 'acquire all of the phase encode steps required to cover k-space (ie one image slice). ' 'For example, if the EPI sequence has 64 phase encode steps, the total readout time is ' 'the time taken to acquire 64 echoes, e.g. ' - 'total readout time = number of echoes × echo spacing. ' + 'total readout time = number of echoes x echo spacing. ' 'This time does not include i) the duration of the excitation, ii) the delay between, ' 'the excitation and the start of the acquisition or iii) time for fat saturation etc.' }'; @@ -72,7 +72,7 @@ epifm = cfg_menu; epifm.tag = 'epifm'; epifm.name = 'EPI-based field map?'; -epifm.help = {'Select non-EPI or EPI based field map. The field map data may be acquired using a non-EPI sequence (typically a gradient echo sequence) or an EPI sequence. The processing will be slightly different for the two cases. If using an EPI-based ï¬eld map, the resulting Voxel Displacement Map will be inverted since the ï¬eld map was acquired in distorted space.'}; +epifm.help = {'Select non-EPI or EPI based field map. The field map data may be acquired using a non-EPI sequence (typically a gradient echo sequence) or an EPI sequence. The processing will be slightly different for the two cases. If using an EPI-based field map, the resulting Voxel Displacement Map will be inverted since the field map was acquired in distorted space.'}; epifm.labels = {'non-EPI' 'EPI'}; epifm.values = {0 1}; diff --git a/toolbox/Longitudinal/tbx_cfg_longitudinal.m b/toolbox/Longitudinal/tbx_cfg_longitudinal.m index befac438..f1f832ed 100644 --- a/toolbox/Longitudinal/tbx_cfg_longitudinal.m +++ b/toolbox/Longitudinal/tbx_cfg_longitudinal.m @@ -4,7 +4,7 @@ % Copyright (C) 2012 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: tbx_cfg_longitudinal.m 5885 2014-02-18 11:53:55Z john $ +% $Id: tbx_cfg_longitudinal.m 6588 2015-11-03 15:26:38Z john $ if ~isdeployed, addpath(fullfile(spm('Dir'),'toolbox','Longitudinal')); @@ -170,7 +170,7 @@ long.val = {vols tim noise wparam bparam write_avg write_jac write_div write_defs}; long.help = {'Longitudinal registration of series of anatomical MRI scans for a single subject. It is based on groupwise alignment among each of the subject''s scans, and incorporates a bias field correction. Prior to running the registration, the scans should already be in very rough alignment, although because the model incorporates a rigid-body transform, this need not be extremely precise. Note that there are a bunch of hyper-parameters to be specified. If you are unsure what values to take, then the defaults should be a reasonable guess of what works. Note that changes to these hyper-parameters will impact the results obtained.',... '',... -'The alignment assumes that all scans have similar resolutions and dimensions, and were collected on the same (or very similar) MR scanner using the same pulse sequence. If these assumption are not correct, then the approach will not work as well.'}; +'The alignment assumes that all scans have similar resolutions and dimensions, and were collected on the same (or very similar) MR scanner using the same pulse sequence. If these assumption are not correct, then the approach will not work as well. There are a number of settings (noise estimate, regularisation etc). Default settings often work well, but it can be very helpful to try some different values, as these can have a large effect on the results.'}; long.prog = @spm_series_align; cfg = cfg_repeat; diff --git a/toolbox/MEEGtools/spm_eeg_fix_ctf_headloc.m b/toolbox/MEEGtools/spm_eeg_fix_ctf_headloc.m index f1428687..dafa7990 100644 --- a/toolbox/MEEGtools/spm_eeg_fix_ctf_headloc.m +++ b/toolbox/MEEGtools/spm_eeg_fix_ctf_headloc.m @@ -26,7 +26,7 @@ % Copyright (C) 2008 Institute of Neurology, UCL % Vladimir Litvak, Robert Oostenveld -% $Id: spm_eeg_fix_ctf_headloc.m 5614 2013-08-15 12:15:16Z vladimir $ +% $Id: spm_eeg_fix_ctf_headloc.m 6663 2016-01-08 16:45:32Z vladimir $ [Finter,Fgraph,CmdLine] = spm('FnUIsetup','Fix CTF head locations',0); @@ -44,6 +44,7 @@ D = spm_eeg_load(S.D); +if ~isfield(S, 'mode'), S.mode = 'interpolate'; end %read HLC-channels %HLC0011 HLC0012 HLC0013 x, y, z coordinates of nasion-coil in m. @@ -141,10 +142,13 @@ OK = [nasOK;nasOK;nasOK; leOK; leOK;leOK; reOK;reOK;reOK]; if min(sum(OK, 2))>2 - fixed = tmpdat; + fixed = tmpdat; + marked = tmpdat; + for i = 1:size(tmpdat, 1) - if any(~OK(i, :)) || (length(tmpind). % -% $Id: spm_eeg_fix_ctf_headloc.m 5614 2013-08-15 12:15:16Z vladimir $ +% $Id: spm_eeg_fix_ctf_headloc.m 6663 2016-01-08 16:45:32Z vladimir $ % My preferred ordering in the grad structure is: % 1st 151 coils are bottom coils of MEG channels diff --git a/toolbox/MEEGtools/spm_eeg_headplot.m b/toolbox/MEEGtools/spm_eeg_headplot.m new file mode 100644 index 00000000..b6040f0c --- /dev/null +++ b/toolbox/MEEGtools/spm_eeg_headplot.m @@ -0,0 +1,902 @@ +function spm_eeg_headplot(Y, D, H) +% SPM interface to headplot function from EEGLAB +% FORMAT spm_eeg_headplot(Y, D, H) +% +% Y - data vector +% D - M/EEG object +% H - (optional) axes handle +% +%__________________________________________________________________________ +% Copyright (C) 2015-2016 Wellcome Trust Centre for Neuroimaging + +% $Id: spm_eeg_headplot.m 6669 2016-01-11 15:51:06Z guillaume $ + +if ~exist('headplot', 'file') + error('This functionality requires EEGLAB http://sccn.ucsd.edu/eeglab/'); +end + +if ~isfield(D, 'inv') + error('Please define a forward model for 3D plotting'); +end + +try + val = D.val; +catch + val = 1; +end + +eegind = 0; +for m = 1:numel(D.inv{val}.forward) + if strncmp('EEG', D.inv{val}.forward(m).modality, 3) + eegind = m; + end +end + +if ~eegind + error('Only EEG modality is supported for 3D plotting.'); +end + +if isfield(D, 'spline_file') + spline_file = D.spline_file; +else + + sens = D.inv{val}.datareg(eegind).sensors; + + elocs = []; + for i = 1:numel(sens.label) + elocs(i).labels = sens.label{i}; + elocs(i).X = sens.chanpos(i, 1); + elocs(i).Y = sens.chanpos(i, 2); + elocs(i).Z = sens.chanpos(i, 3); + end + + spline_file = spm_file(fullfile(D), 'prefix', 'spline_', 'ext', 'spl'); + + headplot('setup', elocs, spline_file); + + D.spline_file = spline_file;save(D); +end + +if nargin>2 + axes(H); +end + +headplot(Y(:)', spline_file, 'electrodes', 'on'); + +function [HeadAxes, ColorbarHandle] = headplot(values, arg1, varargin) + +% headplot() - plot a spherically-splined EEG field map on a semi-realistic +% 3-D head model. Can 3-D rotate the head image using the left +% mouse button. +% Example: +% >> headplot example % show an example spherical 'eloc_angles' file +% >> headplot cartesian % show an example cartesian 'eloc_angles' file +% +% Setup usage (do only once for each scalp montage): +% +% >> headplot('setup', elocs, splinefile, 'Param','Value',...); +% % +% % NOTE: previous call format below is still supported +% % >> headplot('setup', elocs, splinefile, comment, type); +% +% Required Setup-mode Inputs: +% +% elocs - file of electrode locations (compatible with readlocs()), +% or EEG.chanlocs channel location structure. If the channel +% file extension is not standard, use readlocs() to load the +% data file, e.g. +% >> headplot('setup', ... +% readlocs('myfile.xxx', 'filetype', 'besa'),... +% 'splinefile'); +% splinefile - name of spline file to save spline info into. It is saved as a +% *.mat file and should be given the extension .spl . +% +% Optional Setup-mode Inputs: +% +% 'meshfile' - ['string' or structure] Matlab files containing a mesh. The +% mesh may be of different formats. It may be a Dipfit mesh as +% defined in the file standard_vol.mat. It may contain +% a structure with the fields 'vertices' and 'faces' or it +% it may contain a structure with at least two fields: +% POS - 3-D positions of vertices: +% x=left-right; y=back-front; z=up-down +% TRI1 - faces on which the scalp map should be computed +% plus possible optional fields are: +% center (optional) - 3-D center of head mesh +% TRI2 (optional) - faces in skin color +% NORM (optional) - normal for each vertex (better shading) +% 'orilocs' - ['off'|'on'] use original electrode locations on the head +% {default: 'off'} (extrapolated to spherical). Note that these +% electrode locations must be coregisted with the head mesh. +% 'transform' - [real array] Talairach-model transformation matrix to co-register +% the electrode locations with the head mesh: +% [shiftX shiftY shiftZ pitch roll yaw scaleX scaleY scaleZ] +% The transform is applied in the order shift(rotate(scale(elocs))) +% by the dipfit2.* plugin function traditionaldipfit.m +% This array is returned by coregister(). +% 'plotmeshonly' - [string] plot only mesh and electrode positions. Options are +% 'head' to plot the standard head mesh; 'sphere' to plot the +% texture of the head on a sphere; 'off' not to plot anything. +% {default: 'off'} +% 'comment' - ['string'] optional string containing comments for spline file +% {default: []} +% +% Standard-mode Usage thereafter: +% +% >> headplot(values,'spline_file','Param','Value',...) +% +% Required Standard-mode Inputs: +% +% values - vector containing a data value at each electrode position +% 'spline_file' - spline filename, computed and saved in 'setup' mode (above) +% +% Optional Standard-mode Inputs: +% +% 'meshfile' - [string] mesh file name. See file content in the setup-mode +% description above. {default: the EEGLAB head template file}. +% 'electrodes' - ['on'|'off'] -> show electrode positions {default 'on'} +% 'title' - Plot title {default: none} +% 'labels' - 2 -> plot stored electrode labels; +% 1 -> plot channel numbers; 0 -> no labels {default 0} +% 'cbar' - 0 -> Plot colorbar {default: no colorbar} +% Note: standard jet colormap) red = +;blue = -;green=0 +% h -> Colorbar axis handle (to specify headplot location) +% 'view' - Camera viewpoint in deg. [azimuth elevation] +% 'back'|'b'=[ 0 30]; 'front'|'f'=[180 30] +% 'left'|'l'=[-90 30]; 'right'|'r'=[ 90 30]; +% 'frontleft'|'bl','backright'|'br', etc., +% 'top'=[0 90], Can rotate with mouse {default [143 18]} +% 'maplimits' - 'absmax' -> make limits +/- the absolute-max +% 'maxmin' -> scale to data range +% [min,max] -> user-definined values +% {default = 'absmax'} +% 'lights' - (3,N) matrix whose rows give [x y z] pos. of each of +% N lights {default: four lights at corners} +% 'electrode3d' - ['on'|'off'] plot electrodes in 3-D. Default is 'off'. +% 'lighting' - 'off' = show wire frame head {default 'on'} +% 'material' - [see material function] {default 'dull'} +% 'colormap' - 3-column colormap matrix {default: jet(64)} +% 'verbose' - 'off' -> no msgs, no rotate3d {default: 'on'} +% 'orilocs' - [channel structure or channel file name] Use original +% channel locations instead of the one extrapolated from +% spherical locations. Note that if you use 'orilocs' +% during setup, this is not necessary here since the +% original channel location have already been saved. +% This option might be useful to show more channels than +% the ones actually used for interpolating (e.g., fiducials). +% 'transform' - [real array] homogeneous transformation matrix to apply +% to the original locations ('orilocs') before plotting them. +% +% Note: if an error is generated, headplot() may close the current figure +% +% Authors: Arnaud Delorme, Colin Humphries, Scott Makeig, SCCN/INC/UCSD, +% La Jolla, 1998- + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Copyright (C) Arnaud Delorme, Colin Humphries and Scott Makeig, +% CNL / Salk Institute, Feb. 1998 +% +% Spherical spline method: Perrin et al. (1989) Electroenceph clin Neurophys +% +% This program is free software; you can redistribute it and/or modify +% it under the terms of the GNU General Public License as published by +% the Free Software Foundation; either version 2 of the License, or +% (at your option) any later version. +% +% This program is distributed in the hope that it will be useful, +% but WITHOUT ANY WARRANTY; without even the implied warranty of +% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +% GNU General Public License for more details. +% +% You should have received a copy of the GNU General Public License +% along with this program; if not, write to the Free Software +% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +% 12-12-98 changed electrode label lines to MarkerColor -sm +% 12-12-98 added colorbar option -sm (still graphically marred by tan rect.) +% 12-13-98 implemented colorbar option using enhanced cbar -sm +% 12-13-98 implemented 'setup' comment option -sm +% 03-20-00 added cartesian electrode locations option -sm +% 07-14-00 fixed line in calgx() -sm from -ch +% 03-23-01 documented 'cartesian' locfile option -sm +% 01-25-02 reformated help & license, added links -ad +% 03-21-02 added readlocs and the use of eloc input structure -ad + +if nargin < 1 + help headplot + return +end + +%%%%%%%%%%%%%%%%%%%%%%%%%% Set Defaults %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +icadefs % load definitions +set(gca,'Color',BACKCOLOR); +DEFAULT_MESH = ['mheadnew.mat']; % upper head model file (987K) +DEFAULT_TRANSFORM = [0 -5 0 -0.1000 0 -1.5700 1040 800 950]; % stretching in different dimensions +DEFAULT_TRANSFORM = [0 -10 0 -0.1000 0 -1.600 1100 1100 1100]; % keep spherical shape. +%DEFAULT_MESH = '/home/arno/matlab/juliehiresmesh.mat'; +%DEFAULT_MESH = ['/home/scott/matlab/old' '/newupper.mat']; % whole head model file (183K) + +DEFAULT_LIGHTS = [-125 125 80; ... + 125 125 80; ... + 125 -125 125; ... + -125 -125 125]; % default lights at four corners + +HeadCenter = [0 0 30]; +FaceColor = [.8 .55 .35]*1.1; % ~= ruddy Caucasian - pick your complexion! +MAX_ELECTRODES = 1024; +ElectDFac = 1.06; % plot electrode marker dots out from head surface +plotelecopt.NamesDFac = 1.05; % plot electrode names/numbers out from markers +plotelecopt.NamesColor = 'k'; % 'r'; +plotelecopt.NamesSize = 10; % FontSize for electrode names +plotelecopt.MarkerColor= [0.5 0.5 0.5]; +plotelecopt.electrodes3d = 'off'; + +sqaxis = 1; % if non-zero, make head proportions anatomical +title_font = 18; +if isstr(values) + values = lower(values); + if strcmp(values,'setup') + + % + %%%%%%%%%%%%%%%%%%% Perform splining file setup %%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + if nargin < 3 + help headplot; + return; + end; + eloc_file = arg1; + spline_file = varargin{1}; + + g = finputcheck(varargin(2:end), { 'orilocs' 'string' { 'on','off' } 'off'; + 'plotmeshonly' 'string' { 'head','off','sphere' } 'off'; + 'meshfile' { 'string','struct' } [] DEFAULT_MESH; + 'chaninfo' 'struct' [] struct([]); + 'plotchans' 'integer' [] []; + 'ica' 'string' { 'on','off' } 'off'; + 'transform' 'real' [] DEFAULT_TRANSFORM; + 'comment' 'string' [] '' }, 'headplot', 'ignore'); + if isstr(g), + error(g); + end; + + %%%%%%%%%%%%%%%%%%%%%%%%%%% + % Open electrode file + %%%%%%%%%%%%%%%%%%%%%%%%%%% + [eloc_file labels Th Rd indices] = readlocs(eloc_file); + indices = find(~cellfun('isempty', { eloc_file.X })); + + % channels to plot + % ---------------- + if isempty(g.plotchans), g.plotchans = [1:length(eloc_file)]; end; + if ~isfield(g.chaninfo, 'nosedir'), g.chaninfo(1).nosedir = '+x'; end; + indices = intersect_bc(g.plotchans, indices); + + % if ICA select subset of channels if necessary + % --------------------------------------------- + if ~isfield(g.chaninfo, 'icachansind'), g.chaninfo(1).icachansind = 1:length(eloc_file); end; + if strcmpi(g.ica, 'on'), + rmchans2 = setdiff_bc( g.chaninfo.icachansind, indices ); % channels to remove (non-plotted) + newinds = 1:length(g.chaninfo.icachansind); + allrm = []; + % remove non-plotted channels from indices + for index = 1:length(rmchans2) + chanind = find(g.chaninfo.icachansind == rmchans2(index)); + allrm = [ allrm chanind ]; + end; + + newinds(allrm) = []; + indices = newinds; + eloc_file = eloc_file(g.chaninfo.icachansind); + end; + + fprintf('Headplot: using existing XYZ coordinates\n'); + ElectrodeNames = strvcat({ eloc_file.labels }); + ElectrodeNames = ElectrodeNames(indices,:); + + Xeori = [ eloc_file(indices).X ]'; + Yeori = [ eloc_file(indices).Y ]'; + Zeori = [ eloc_file(indices).Z ]'; + + [newPOS POS TRI1 TRI2 NORM index1 center] = getMeshData(g.meshfile); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % rotate channel coordinates if necessary + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if strcmpi(lower(g.chaninfo.nosedir), '+x') + rotate = 0; + else + if strcmpi(lower(g.chaninfo.nosedir), '+y') + rotate = 3*pi/2; + elseif strcmpi(lower(g.chaninfo.nosedir), '-x') + rotate = pi; + else rotate = pi/2; + end; + allcoords = (Yeori + Xeori*sqrt(-1))*exp(sqrt(-1)*rotate); + Xeori = imag(allcoords); + Yeori = real(allcoords); + end; + newcoords = [ Xeori Yeori Zeori ]; + + %newcoords = transformcoords( [ Xe Ye Ze ], [0 -pi/16 -1.57], 100, -[6 0 46]); + %newcoords = transformcoords( [ Xeori Yeori Zeori ], g.transform(4:6), g.transform(7:9), g.transform(1:3)); + % same performed below with homogenous transformation matrix + + transmat = eye(4);%traditionaldipfit( g.transform ); % arno + newcoords = transmat*[ newcoords ones(size(newcoords,1),1)]'; + newcoords = newcoords(1:3,:)'; + + % original center was [6 0 16] but the center of the sphere is [0 0 30] + % which compensate (see variable Headcenter) + %newcoords = transformcoords( [ Xe Ye Ze ], -[0 0 -pi/6]); + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % normalize with respect to head center + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + newcoordsnorm = newcoords - ones(size(newcoords,1),1)*HeadCenter; + tmpnorm = sqrt(sum(newcoordsnorm.^2,2)); + Xe = newcoordsnorm(:,1)./tmpnorm; + Ye = newcoordsnorm(:,2)./tmpnorm; + Ze = newcoordsnorm(:,3)./tmpnorm; + %plotchans3d([ Xe Ye Ze], cellstr(ElectrodeNames)); return; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Calculate g(x) for electrodes + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if strcmpi(g.plotmeshonly, 'off') + fprintf('Setting up splining matrix.\n'); + enum = length(Xe); + onemat = ones(enum,1); + G = zeros(enum,enum); + for i = 1:enum + ei = onemat-sqrt((Xe(i)*onemat-Xe).^2 + (Ye(i)*onemat-Ye).^2 + ... + (Ze(i)*onemat-Ze).^2); % default was /2 and no sqrt + gx = zeros(1,enum); + for j = 1:enum + gx(j) = calcgx(ei(j)); + end + G(i,:) = gx; + end + end; + fprintf('Calculating splining matrix...\n') + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Open mesh file - contains POS and index1 + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Project head vertices onto unit sphere + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + spherePOS = newPOS-ones(size(newPOS,1),1)*HeadCenter; % recenter + nPOSnorm = sqrt(sum(spherePOS.^2,2)); + spherePOS(:,1) = spherePOS(:,1)./nPOSnorm; + spherePOS(:,2) = spherePOS(:,2)./nPOSnorm; + spherePOS(:,3) = spherePOS(:,3)./nPOSnorm; + x = spherePOS(:,1); + y = spherePOS(:,2); + z = spherePOS(:,3); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Calculate new electrode positions on head + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if strcmpi(g.orilocs, 'off') + fprintf('Computing electrode locations on head...\n'); + for i=1:length(Xe) + elect = [Xe(i) Ye(i) Ze(i)]; + dists = distance(elect,spherePOS'); + [S,I] = sort(dists); + npoints = I(1:3); % closest 3 points + diffe = newPOS(npoints,:)-spherePOS(npoints,:); + newElect(i,:) = elect+mean(diffe)*ElectDFac; + %if Ze(i) < 0 % Plot superior electrodes only. + % newElect(i,:) = [0 0 0]; % Mark lower electrodes as having + %end % an electrode position not to be plotted + end + else + fprintf('Using original electrode locations on head...\n'); + newElect = newcoords; + end; + + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % plot mesh and electrodes only + % %%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if ~strcmpi(g.plotmeshonly, 'off') + if strcmpi(g.plotmeshonly, 'sphere') + newElect(:,1) = Xe; + newElect(:,2) = Ye; + newElect(:,3) = Ze; + POS(index1,:) = spherePOS; HeadCenter = [ 0 0 0 ]; + end; + plotmesh(TRI1, POS, NORM); + plotelecopt.labelflag = 0; + plotelec(newElect, ElectrodeNames, HeadCenter, plotelecopt); + rotate3d; + return; + end; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Calculate g(x) for sphere mesh vertices + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + fprintf('Computing %d vertices. Should take a while (see wait bar)\n',... + length(x)) + fprintf(' but doesnt have to be done again for this montage...\n'); + icadefs; + + gx = fastcalcgx(x,y,z,Xe,Ye,Ze); + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Save spline file + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + comment = g.comment; + headplot_version = 2; + transform = g.transform; + try, save(spline_file, '-V6', '-mat', 'Xe', 'Ye', 'Ze', 'G', 'gx', 'newElect', ... + 'ElectrodeNames', 'indices', 'comment', 'headplot_version', 'transform'); + catch, + try, save(spline_file, '-mat', 'Xe', 'Ye', 'Ze', 'G', 'gx', 'newElect', ... + 'ElectrodeNames', 'indices', 'comment', 'headplot_version', 'transform'); + catch, error('headplot: save spline file error, out of space or file permission problem'); + end; + end; + tmpinfo = dir(spline_file); + fprintf('Saving (%dk) file %s\n',round(tmpinfo.bytes/1000), spline_file); + return + + elseif strcmp(values,'example') | strcmp(values,'demo') + % + %%%%%%%%%%%%%%%%%% Show an example electrode angles file %%%%%%%%%%%%%%%%%%%%%%%% + % + fprintf(['\nExample of a headplot() electrode angles file (spherical coords.)\n',... + 'Fields: chan_num cor_deg horiz_deg channel_name\n\n',... + ' 1 -90 -72 Fp1.\n',... + ' 2 90 72 Fp2.\n',... + ' 3 -62 -57 F3..\n',... + ' 4 62 57 F4..\n',... + ' 5 -45 0 C3..\n',... + ' 6 45 0 C4..\n',... + ' 7 -118 2 A1..\n',... + ' 8 118 -2 A2..\n',... + ' 9 -62 57 P3..\n',... + ' 10 62 -57 P4..\n',... + ' 11 -90 72 O1..\n',... + ' 12 90 -72 O2..\n',... + ' 13 -90 -36 F7..\n',... + ' 14 90 36 F8..\n',... + ' 15 -90 0 T3..\n',... + ' 16 90 0 T4..\n',... + ' 17 -90 36 T5..\n',... + ' 18 90 -36 T6..\n',... + ' 19 45 90 Fz..\n',... + ' 20 0 0 Cz..\n',... + ' 21 45 -90 Pz..\n',... + '\nA 90 deg coronal rotation points to right ear, -90 to left.\n' ,... + 'A positive horizontal rotation is counterclockwise from above.\n',... + 'Use pol2sph() to convert from topoplot() format to spherical.\n',... + 'Channel names should have 4 chars (. = space).\n',... + 'See also >> headplot cartesian\n\n\n']); + return + elseif strcmp(values,'cartesian') + % + %%%%%%%%%%%%%%%%%% Show an example cartesian electrode file %%%%%%%%%%%%%%%%%%% + % + fprintf(['\nExample of a headplot() electrode location file (cartesian coords.)\n',... + 'Fields: chan_num x y z channel_name\n\n',... + ' 1 0.4528 0.8888 -0.0694 Fp1.\n',... + 'Channel names should have 4 chars (. = space).\n',... + 'See also >> headplot example\n\n\n']); + return + else + fprintf('headplot(): Unknown first argument (%s).\n',values) + help headplot + end +else + % + %%%%%%%%%%%%%%%%%%%%%%%%%% Make the plot %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % + if nargin < 2 + help headplot + return + end + spline_file = arg1; + + g = finputcheck( varargin, { ... + 'cbar' 'real' [0 Inf] []; % Colorbar value must be 0 or axis handle.' + 'lighting' 'string' { 'on','off' } 'on'; + 'verbose' 'string' { 'on','off' } 'on'; + 'maplimits' { 'string','real' } [] 'absmax'; + 'title' 'string' [] ''; + 'lights' 'real' [] DEFAULT_LIGHTS; + 'view' { 'string','real' } [] [143 18]; + 'colormap' 'real' [] jet(256); + 'transform' 'real' [] []; + 'meshfile' {'string','struct' } [] DEFAULT_MESH; + 'electrodes' 'string' { 'on','off' } 'on'; + 'electrodes3d' 'string' { 'on','off' } 'off'; + 'material' 'string' [] 'dull'; + 'orilocs' { 'string','struct' } [] ''; + 'labels' 'integer' [0 1 2] 0 }, 'headplot'); + if isstr(g) error(g); end; + plotelecopt.electrodes3d = g.electrodes3d; + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Open head mesh and electrode spline files + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + if ~exist(spline_file) + error(sprintf('headplot(): spline_file "%s" not found. Run headplot in "setup" mode\n',... + spline_file)); + end + load(spline_file, '-mat'); + if exist('indices'), + try, + values = values(indices); + catch, error('problem of index or electrode number with splinefile'); end; + end; + enum = length(values); + if enum ~= length(Xe) + close; + error('headplot(): Number of values in spline file should equal number of electrodes') + end + + % change electrode if necessary + % ----------------------------- + if ~isempty(g.orilocs) + eloc_file = readlocs( g.orilocs ); + fprintf('Using original electrode locations on head...\n'); + indices = find(~cellfun('isempty', { eloc_file.X } )); + newElect(:,1) = [ eloc_file(indices).X ]'; % attention inversion before + newElect(:,2) = [ eloc_file(indices).Y ]'; + newElect(:,3) = [ eloc_file(indices).Z ]'; + + % optional transformation + % ----------------------- + if ~isempty(g.transform) + transmat = traditionaldipfit( g.transform ); % arno + newElect = transmat*[ newElect ones(size(newElect,1),1)]'; + newElect = newElect(1:3,:)'; + end; + end; + + % -------------- + % load mesh file + % -------------- + [newPOS POS TRI1 TRI2 NORM index1 center] = getMeshData(g.meshfile); + + %%%%%%%%%%%%%%%%%%%%%%%%%% + % Perform interpolation + %%%%%%%%%%%%%%%%%%%%%%%%%% + + meanval = mean(values); values = values - meanval; % make mean zero + onemat = ones(enum,1); + lamd = 0.1; + C = pinv([(G + lamd);ones(1,enum)]) * [values(:);0]; % fixing division error + P = zeros(1,size(gx,1)); + for j = 1:size(gx,1) + P(j) = dot(C,gx(j,:)); + end + P = P + meanval; + + %%%%%%%%%%%%%%%%%%%%%%%%%% + % Plot surfaces + %%%%%%%%%%%%%%%%%%%%%%%%%% + cla % clear axis + HeadAxes = gca; + + W = zeros(1,size(POS,1)); + m = size(g.colormap,1); + if size(g.maplimits) == [1,2] + amin = g.maplimits(1); + amax = g.maplimits(2); + elseif strcmp(g.maplimits,'maxmin') | strcmp(g.maplimits,'minmax') + amin = min(min(abs(P)))*1.02; % 2% shrinkage keeps within color bounds + amax = max(max(abs(P)))*1.02; + elseif strcmp(g.maplimits,'absmax') + amin = min(min(abs(P)))*1.02; % 2% shrinkage keeps within color bounds + amax = max(max(abs(P)))*1.02; + amax = max(-amin, amax); + amin = -amax; + %amin = -max(max(abs(P)))*1.02; % 2% shrinkage keeps within color bounds + %amax = -amin; + end + + idx = min(m,round((m-1)*(P-amin)/(amax-amin))+1); % get colormap indices + %subplot(1,2,1); hist(P(:)); + %idx = round((m-1)*P/(amax-amin))+m/2; + %idx = max(1,min(m,idx)); % get colormap indices + %subplot(1,2,2); hist(idx(:)); + %return; + + W(index1) = idx; + colormap(g.colormap) + p1 = patch('Vertices',POS,'Faces',TRI1,'FaceVertexCdata',W(:),... + 'FaceColor','interp', 'cdatamapping', 'direct', 'tag', 'mesh'); %%%%%%%%% Plot scalp map %%%%%%%%% + if exist('NORM') == 1 & ~isempty(NORM) + set(p1, 'vertexnormals', NORM); + end; + + if ~isempty(TRI2) + FCmap = [g.colormap; g.colormap(end,:); FaceColor; FaceColor; FaceColor]; + colormap(FCmap) + W = ones(1,size(POS,1))*(m+2); + p2 = patch('Vertices',POS,'Faces',TRI2,'FaceColor','interp',... + 'FaceVertexCdata',W(:)); %%%%%%%% Plot face and lower head %%%%%% + else + p2 = []; + end; + + axis([-125 125 -125 125 -125 125]) + axis off % hide axis frame + + %%%%%%%%%%%%%%%%%%%%%%%%% + % Draw colorbar - Note: uses enhanced cbar() function by Colin Humphries + %%%%%%%%%%%%%%%%%%%%%%%%% + if ~isempty(g.cbar) + BACKCOLOR = get(gcf,'Color'); + if g.cbar == 0 + ColorbarHandle = cbar(0,3,[amin amax]); + pos = get(ColorbarHandle,'position'); % move left & shrink to match head size + set(ColorbarHandle,'position',[pos(1)-.05 pos(2)+0.13 pos(3)*0.7 pos(4)-0.26]); + else + ColorbarHandle = cbar(g.cbar,3,[amin amax]); + end + end + axes(HeadAxes); + + %%%%%%%%%%%%%%%%%%%%%%%%% + % Turn on lights + %%%%%%%%%%%%%%%%%%%%%%%%% + + if strcmp(g.lighting,'on') + set([p1 p2],'EdgeColor','none') + + for i = 1:size(g.lights,1) + hl(i) = light('Position',g.lights(i,:),'Color',[1 1 1],... + 'Style','infinite'); + end + if ~isempty(p2) + set(p2,'DiffuseStrength',.6,'SpecularStrength',0,... + 'AmbientStrength',.4,'SpecularExponent',5) + end; + set(p1,'DiffuseStrength',.6,'SpecularStrength',0,... + 'AmbientStrength',.3,'SpecularExponent',5) + lighting phong % all this gives a matte reflectance + material(g.material); + end + + %%%%%%%%%%%%%%%%%%%%%%%%% + % Set viewpoint + %%%%%%%%%%%%%%%%%%%%%%%%% + + if isstr(g.view) + switch lower(g.view) + case {'front','f'} + view(-180,30) + case {'back','b'} + view(0,30) + case {'left','l'} + view(-90,30) + case {'right','r'} + view(90,30) + case {'frontright','fr'} + view(135,30) + case {'backright','br'} + view(45,30) + case {'frontleft','fl'} + view(-135,30) + case {'backleft','bl'} + view(-45,30) + case 'top' + view(0,90) + case 'bottom' % undocumented option! + view(0,-90) + Lights = [-125 125 80; ... + 125 125 80; ... + 125 -125 125; ... + -125 -125 125; ... + 0 10 -80]; % add light from below! + otherwise + close; error(['headplot(): Invalid View value %s',g.view]) + end + else + if ~isstr(g.view) + [h,a] = size(g.view); + if h~= 1 | a~=2 + close; error('headplot(): View matrix size must be (1,2).') + end + end + view(g.view) % set camera viewpoint + end + + if strcmp(g.electrodes,'on') % plot the electrode locations + if exist('newElect') + plotelecopt.labelflag = g.labels; + plotelec(newElect, ElectrodeNames, HeadCenter, plotelecopt); + else + fprintf('Variable newElect not read from spline file.\n'); + end + end + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + % Turn on rotate3d, allowing rotation of the plot using the mouse + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + if strcmp(g.verbose,'on') + rotate3d on; % Allow 3-D rotation of the plot by dragging the + else % left mouse button while cursor is on the plot + rotate3d off + end + % Make axis square + if sqaxis + axis image % keep the head proportions human and as large as possible + end + % Add a plot title + if ~isempty(g.title); + % title(['\n' g.title],'fontsize',title_font); + title([g.title],'fontsize',title_font); % Note: \n not interpreted by matlab-5.2 + end +end + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% calcgx() - function used in 'setup' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function [out] = calcgx(in) + +out = 0; +m = 4; % 4th degree Legendre polynomial +for n = 1:7 % compute 7 terms + L = legendre(n,in); + out = out + ((2*n+1)/(n^m*(n+1)^m))*L(1); +end +out = out/(4*pi); + + +%%%%%%%%%%%%%%%%%%% +function gx = fastcalcgx(x,y,z,Xe,Ye,Ze) + +onemat = ones(length(x),length(Xe)); +EI = onemat - sqrt((repmat(x,1,length(Xe)) - repmat(Xe',length(x),1)).^2 +... + (repmat(y,1,length(Xe)) - repmat(Ye',length(x),1)).^2 +... + (repmat(z,1,length(Xe)) - repmat(Ze',length(x),1)).^2); +% +gx = zeros(length(x),length(Xe)); +m = 4; +icadefs; +hwb = waitbar(0,'Computing spline file (only done once)...', 'color', BACKEEGLABCOLOR); +hwbend = 7; +for n = 1:7 + L = legendre(n,EI); + gx = gx + ((2*n+1)/(n^m*(n+1)^m))*squeeze(L(1,:,:)); + waitbar(n/hwbend,hwb); +end +gx = gx/(4*pi); +close(hwb); + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% distance() - function used in 'setup' +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +function [out] = distance(w,p) +% w is a matrix of row vectors +% p is a matrix of column vectors + +l1 = size(w,1); +l2 = size(p,2); +out = zeros(l1,l2); + +for i = 1:l1 + x = w(i,:)'*ones(1,l2); + out(i,:) = sum((x-p).^2).^.5; +end + +% %%%%%%%%%%%%%%% +% plot electrodes +% %%%%%%%%%%%%%%% +function plotelec(newElect, ElectrodeNames, HeadCenter, opt); + +newNames = newElect*opt.NamesDFac; % Calculate electrode label positions +for i = 1:size(newElect,1) + if newElect(i,:) ~= [0 0 0] % plot radial lines to electrode sites + if strcmpi(opt.electrodes3d, 'off') + line([newElect(i,1) HeadCenter(1)],[newElect(i,2) HeadCenter(2)],... + [newElect(i,3) HeadCenter(3)],'color',opt.MarkerColor,'linewidth',1); + end; + if opt.labelflag == 1 % plot electrode numbers + t=text(newNames(i,1),newNames(i,2),newNames(i,3),int2str(i)); + set(t,'Color',opt.NamesColor,'FontSize',opt.NamesSize,'FontWeight','bold',... + 'HorizontalAlignment','center'); + + elseif opt.labelflag == 2 % plot electrode names + if exist('ElectrodeNames') + name = sprintf('%s',ElectrodeNames(i,:)); + t=text(newNames(i,1),newNames(i,2),newNames(i,3),name); + set(t,'Color',opt.NamesColor,'FontSize',opt.NamesSize,'FontWeight','bold',... + 'HorizontalAlignment','center'); + else + fprintf('Variable ElectrodeNames not read from spline file.\n'); + end + else % plot electrode markers + + if strcmpi(opt.electrodes3d, 'off') + line(newElect(:,1),newElect(:,2),newElect(:,3),'marker',... + '.','markersize',20,'color',opt.MarkerColor,'linestyle','none'); + else + [xc yc zc] = cylinder( 2, 10); + [xs ys zs] = sphere(10); + xc = [ xc; -xs(7:11,:)*2 ]; + yc = [ yc; -ys(7:11,:)*2 ]; + zc = [ zc; zs(7:11,:)*0.2+1 ]; + + hold on; + cylinderSize = 3; + colorarray = repmat(reshape(opt.MarkerColor, 1,1,3), [size(zc,1) size(zc,2) 1]); + handles = surf(xc*cylinderSize, yc*cylinderSize, zc*cylinderSize, colorarray, 'edgecolor', 'none', ... + 'backfacelighting', 'lit', 'facecolor', 'interp', 'facelighting', ... + 'phong', 'ambientstrength', 0.3); + + cylnderHeight = 1; + if newElect(i,3) < 10, addZ = -30; else addZ = 0; end; + if newElect(i,3) < -20, addZ = -60; else addZ = 0; end; + xx = newElect(i,1) - ( newElect(i,1)-HeadCenter(1) ) * 0.01 * cylnderHeight; + xxo1 = newElect(i,1) + ( newElect(i,1)-HeadCenter(1) ) * 0.01 * cylnderHeight; + yy = newElect(i,2) - ( newElect(i,2)-HeadCenter(2) ) * 0.01 * cylnderHeight; + yyo1 = newElect(i,2) + ( newElect(i,2)-HeadCenter(2) ) * 0.01 * cylnderHeight; + zz = newElect(i,3) - ( newElect(i,3)-HeadCenter(3)-addZ ) * 0.01 * cylnderHeight; + zzo1 = newElect(i,3) + ( newElect(i,3)-HeadCenter(3)-addZ ) * 0.01 * cylnderHeight; + [xc yc zc] = adjustcylinder2( handles, [xx yy zz], [xxo1 yyo1 zzo1] ); + end; + + end + end +end; + +% get mesh information +% -------------------- +function [newPOS POS TRI1 TRI2 NORM index1 center] = getMeshData(meshfile); + +if ~isstruct(meshfile) + if ~exist(meshfile) + error(sprintf('headplot(): mesh file "%s" not found\n',meshfile)); + end + fprintf('Loaded mesh file %s\n',meshfile); + try + meshfile = load(meshfile,'-mat'); + catch, + meshfile = []; + meshfile.POS = load('mheadnewpos.txt', '-ascii'); + meshfile.TRI1 = load('mheadnewtri1.txt', '-ascii'); % upper head + %try, TRI2 = load('mheadnewtri2.txt', '-ascii'); catch, end; % lower head + %index1 = load('mheadnewindex1.txt', '-ascii'); + meshfile.center = load('mheadnewcenter.txt', '-ascii'); + end; +end; + +if isfield(meshfile, 'vol') + if isfield(meshfile.vol, 'r') + [X Y Z] = sphere(50); + POS = { X*max(meshfile.vol.r) Y*max(meshfile.vol.r) Z*max(meshfile.vol.r) }; + TRI1 = []; + else + POS = meshfile.vol.bnd(1).pnt; + TRI1 = meshfile.vol.bnd(1).tri; + end; +elseif isfield(meshfile, 'bnd') + POS = meshfile.bnd(1).pnt; + TRI1 = meshfile.bnd(1).tri; +elseif isfield(meshfile, 'TRI1') + POS = meshfile.POS; + TRI1 = meshfile.TRI1; + try TRI2 = meshfile.TRI2; end % NEW + try center = meshfile.center; end % NEW +elseif isfield(meshfile, 'vertices') + POS = meshfile.vertices; + TRI1 = meshfile.faces; +else + error('Unknown Matlab mesh file'); +end; +if exist('index1') ~= 1, index1 = sort(unique(TRI1(:))); end; +if exist('TRI2') ~= 1, TRI2 = []; end; +if exist('NORM') ~= 1, NORM = []; end; +if exist('TRI1') ~= 1, error('Variable ''TRI1'' not defined in mesh file'); end; +if exist('POS') ~= 1, error('Variable ''POS'' not defined in mesh file'); end; +if exist('center') ~= 1, center = [0 0 0]; disp('Using [0 0 0] for center of head mesh'); end; +newPOS = POS(index1,:); + diff --git a/toolbox/MEEGtools/spm_eeg_img2maps.m b/toolbox/MEEGtools/spm_eeg_img2maps.m index d22ded04..0d8af3db 100644 --- a/toolbox/MEEGtools/spm_eeg_img2maps.m +++ b/toolbox/MEEGtools/spm_eeg_img2maps.m @@ -12,9 +12,9 @@ function spm_eeg_img2maps(S) % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Vladimir Litvak -% $Id: spm_eeg_img2maps.m 5640 2013-09-18 12:02:29Z vladimir $ +% $Id: spm_eeg_img2maps.m 6669 2016-01-11 15:51:06Z guillaume $ -SVNrev = '$Rev: 5640 $'; +SVNrev = '$Rev: 6669 $'; %-Startup %-------------------------------------------------------------------------- @@ -48,6 +48,9 @@ function spm_eeg_img2maps(S) S.window = spm_input('start and end of window [ms or Hz]', '+1', 'r', '', 2); end +style = char(spm_input('Plot style','+1','SPM|FT|3D',{'spm','ft','3d'},1)); + + V = spm_vol(S.image); Y = spm_read_vols(V); @@ -77,14 +80,11 @@ function spm_eeg_img2maps(S) %-Get channel indices and coordinates %-------------------------------------------------------------------------- -[Cel, Cind] = spm_eeg_locate_channels(D, n, 1); - modality = spm_eeg_modality_ui(D, 1, 1); -goodchan = find(ismember(Cind, D.indchantype(modality, 'GOOD'))); +Cind = D.indchantype(modality, 'GOOD'); -Cel = Cel(goodchan, :); -Cind = Cind(goodchan); +Cel = spm_eeg_locate_channels(D, n, Cind); if isempty(Cind) error('No good channels to plot'); @@ -98,19 +98,46 @@ function spm_eeg_img2maps(S) %-------------------------------------------------------------------------- Fgraph = spm_figure('GetWin','Graphics'); spm_figure('Clear',Fgraph) -if ~isfield(S, 'clim') - in.max = max(abs(Y)); - in.min = -in.max; -else - in.min = S.clim(1); - in.max = S.clim(2); -end - -in.type = modality; -in.f = Fgraph; -in.ParentAxes = axes; - -spm_eeg_plotScalpData(Y, D.coor2D(Cind), D.chanlabels(Cind), in); +switch style + case 'spm' + if ~isfield(S, 'clim') + in.max = max(abs(Y)); + in.min = -in.max; + else + in.min = S.clim(1); + in.max = S.clim(2); + end + + in.type = modality; + in.f = Fgraph; + in.ParentAxes = axes; + + spm_eeg_plotScalpData(Y, D.coor2D(Cind), D.chanlabels(Cind), in); + case 'ft' + + dummy = []; + dummy.dimord = 'chan_time'; + dummy.avg = Y; + dummy.label = D.chanlabels(Cind); + dummy.time = 1e-3*mean(S.window); + + cfg = []; + cfg.parameter = 'avg'; + cfg.comment = 'no'; + + switch modality + case 'EEG' + cfg.elec = D.sensors('EEG'); + case 'MEG' + cfg.grad = D.sensors('MEG'); + end + cfg.zlim = max(abs(Y))*[-1 1]; + + ft_topoplotER(cfg, dummy); + + case '3d' + spm_eeg_headplot(Y, D, axes); +end %% %-Cleanup %-------------------------------------------------------------------------- diff --git a/toolbox/Neural_Models/Neural_demo.fig b/toolbox/Neural_Models/Neural_demo.fig index 71bc6087..eab8596a 100644 Binary files a/toolbox/Neural_Models/Neural_demo.fig and b/toolbox/Neural_Models/Neural_demo.fig differ diff --git a/toolbox/Neural_Models/Neural_demo.m b/toolbox/Neural_Models/Neural_demo.m index 25f6ef4f..55ada8a8 100644 --- a/toolbox/Neural_Models/Neural_demo.m +++ b/toolbox/Neural_Models/Neural_demo.m @@ -22,7 +22,7 @@ % Edit the above text to modify the response to help Neural_demo -% Last Modified by GUIDE v2.5 27-Jun-2014 14:48:18 +% Last Modified by GUIDE v2.5 16-Jun-2015 17:40:07 % Begin initialization code - DO NOT EDIT gui_Singleton = 1; @@ -196,3 +196,9 @@ function pushbutton32_Callback(hObject, eventdata, handles) % --- Executes on button press in pushbutton33. function pushbutton33_Callback(hObject, eventdata, handles) run_demo_Callback(hObject, handles, 'spm_epileptor_demo') + + +% --- Executes on button press in pushbutton42. +function pushbutton42_Callback(hObject, eventdata, handles) +run_demo_Callback(hObject, handles, 'spm_erp2csd_demo') + diff --git a/toolbox/Neural_Models/spm_erp2csd_demo.m b/toolbox/Neural_Models/spm_erp2csd_demo.m new file mode 100644 index 00000000..ece4665e --- /dev/null +++ b/toolbox/Neural_Models/spm_erp2csd_demo.m @@ -0,0 +1,337 @@ +function spm_erp2csd_demo +% Demo routine for local field potential models +%========================================================================== +% +% This routine illustrates the use of empirical Bayes, for dynamic causal +% modelling, in identifying the causes of paroxysmal seizure activity; as +% expressed in terms of spectral density responses. We first simulate data +% by generating (endogenous neuronal) inputs under a scale free or power +% law assumption (the priors used for DCM for CSD). The inputs are used to +% generate responses over two seconds, whose spectral density is then used +% to estimate the neural mass model parameters. This is repeated for +% several different values of a particular intrinsic connection strength. +% Empirical Bayes is then used to compare competing models of between +% epoch changes in intrinsic connections. The posterior distributions +% are then compared with the true values, under the selected model. +% +% The key aspects of this demonstration are to show that cross spectral +% density data features can be used to summarise evoked responses – and +% that trial to trial (or condition to condition) variations in model +% parameters can be identified using model selection, under a parametric +% random effect or empirical Bayesian model, which furnishes posterior +% densities over parameters at the first or within trial Level. +% +%__________________________________________________________________________ +% Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging + +% Karl Friston +% $Id: spm_erp2csd_demo.m 6506 2015-07-24 10:26:51Z karl $ + + +% Model specification +%========================================================================== +rng('default') + +% number of regions in simulated seizure activity and model specification +%-------------------------------------------------------------------------- +rG = [0,2]; % range of parameter +Nc = 1; % number of channels +Ns = 1; % number of sources +options.spatial = 'LFP'; +options.model = 'CMC'; +options.analysis = 'CSD'; +M.dipfit.model = options.model; +M.dipfit.type = options.spatial; +M.dipfit.Nc = Nc; +M.dipfit.Ns = Ns; +M.Hz = 1:128; + +% get associated priors +%-------------------------------------------------------------------------- +A = {0 0 0}; +B = {}; +C = 1; +pE = spm_dcm_neural_priors(A,B,C,options.model); +pE = spm_L_priors(M.dipfit,pE); +pE = spm_ssr_priors(pE); +[x,f] = spm_dcm_x_neural(pE,options.model); + +% suppress channel noise (assuming many trials would be averaged) +%-------------------------------------------------------------------------- +pE.a = [ 0; 0]; % log amplitude and f^(-a) exponent +pE.b = [-2; 0]; % log amplitude and f^(-a) exponent +pE.c = [-2; 0]; % log amplitude and f^(-a) exponent + +% number of hidden states and endogenous inputs +%-------------------------------------------------------------------------- +nx = length(spm_vec(x)); +nu = size(pE.C,2); + +% create LFP model +%========================================================================== +M.f = f; +M.g = 'spm_gx_erp'; +M.x = x; +M.n = nx; +M.pE = pE; +M.m = nu; +M.l = Nc; + +% Volterra Kernels and transfer functions +%========================================================================== +% illustrate response characteristics in terms of Volterra Kernels and +% transfer functions. The idea here is to illustrate the dependency on a +% particular connection parameter; here the inhibitory connection between +% superficial pyramidal cells and spiny stellate cells. +%-------------------------------------------------------------------------- +spm_figure('GetWin','Volterra kernels and transfer functions'); + +% augment and bi-linearise (with delays) +%-------------------------------------------------------------------------- +[f,J,D] = spm_fx_cmc(x,0,pE,M); +M.u = sparse(Ns,1); +M.D = D; +[M0,M1,L1,L2] = spm_bireduce(M,pE); + +% compute kernels (over 64 ms) +%-------------------------------------------------------------------------- +N = 64; +dt = 1/1000; +pst = (1:N)*dt*1000; +[K0,K1,K2] = spm_kernels(M0,M1,L1,L2,N,dt); + +subplot(2,2,1) +plot(pst,K1(:,:,1)) +title('1st-order Volterra kernel','FontSize',16) +axis square +xlabel('time (ms)') + +subplot(2,2,2) +imagesc(pst,pst,K2(1:64,1:64,1,1,1)) +title('2nd-order Volterra kernel','FontSize',16) +axis square +xlabel('time (ms)') + +% compute transfer functions for different inhibitory connections +%-------------------------------------------------------------------------- +p = linspace(-1,3,64); +for i = 1:length(p) + P = pE; + P.G(3) = p(i); + [G,w] = spm_csd_mtf(P,M); + GW(:,i) = abs(G{1}); +end + +subplot(2,2,3) +plot(w,GW) +xlabel('frequency {Hz}') +title('transfer function','FontSize',16) +axis square + +subplot(2,2,4) +imagesc(p,w,log(GW)), hold on +plot([rG(1),rG(1)],[w(1),w(end)],':w'), hold on +plot([rG(2),rG(2)],[w(1),w(end)],':w'), hold off +title('transfer functions','FontSize',16) +ylabel('Frequency') +xlabel('Inhibitory connection','FontSize',16) +axis xy +axis square + + +% illustrate responses to random fluctuations +%========================================================================== +spm_figure('GetWin','spontaneous fluctuations');clf + +U.dt = 4/1000; +N = 2/U.dt; +pst = (1:N)'*U.dt; + + +% spectral densities of inputs and noise (under prior expectations) +%-------------------------------------------------------------------------- +[Gu,Gs] = spm_csd_mtf_gu(pE,M.Hz); +[n,Kn] = spm_rand_power_law(Gs,M.Hz,U.dt,N); +[u,Ku] = spm_rand_power_law(Gu,M.Hz,U.dt,N); + +% generate data under increasing values (G) of the inhibitory connection: +%-------------------------------------------------------------------------- +nt = 8; +M.p = 8; +M.dt = U.dt; +G = linspace(rG(1),rG(2),4); +for i = 1:length(G) + + % change intrisic connectivity + %---------------------------------------------------------------------- + P = pE; + P.G(3) = G(i); + + % predicted spectral density + %---------------------------------------------------------------------- + psd(i) = spm_csd_mtf(P,M,U); + + % sample density over (nt) trials + %---------------------------------------------------------------------- + csd{i} = 0; + for j = 1:nt + + % generate random neuronal fluctuations - u (and noise - n) + %------------------------------------------------------------------ + n = randn(N,1); + n = Kn*n; + u = randn(N,1); u(fix(N/8)) = 16; + u = Ku*u; + + + % generated evoked response and estimate induced response (with noise) + %------------------------------------------------------------------ + U.u = u; + y = spm_int_L(P,M,U); + mar = spm_mar(y + n,M.p); + mar = spm_mar_spectra(mar,M.Hz,1/U.dt); + csd{i} = csd{i} + mar.P/nt; + + end + + % plot + %---------------------------------------------------------------------- + col = [1/2 1 1]*(1 - i/length(G)); + t = find(pst < .5); + + if i == 1 + + subplot(3,2,1), plot(pst(t),u(t),'Color',col), spm_axis tight + xlabel('time (s)'), title(' neuronal input','FontSize',16) + + % LFP – random fluctuations + %---------------------------------------------------------------------- + subplot(3,2,2), plot(pst(t),y(t),'Color',col), hold on + plot(pst(t),n(t),':','Color',col), hold off, spm_axis tight + xlabel('time (s)'), title('LFP response (low connectivity)','FontSize',16) + + + elseif i == length(G) + + subplot(3,2,3), plot(pst(t),u(t),'Color',col), spm_axis tight + xlabel('time (s)'), title(' neuronal input','FontSize',16) + + % LFP – random fluctuations + %---------------------------------------------------------------------- + subplot(3,2,4), plot(pst(t),y(t),'Color',col), hold on + plot(pst(t),n(t),':','Color',col), hold on, spm_axis tight + xlabel('time (s)'), title('(high connectivity)','FontSize',16) + + end + + + % plot spectral density with changes in connectivity + %---------------------------------------------------------------------- + subplot(3,2,5), plot(M.Hz,abs(csd{i}),'Color',col), hold on + xlabel('time (s)'), title('spectral density','FontSize',16) + spm_axis tight + + subplot(3,2,6), plot(M.Hz,abs(psd{i}),'Color',col), hold on + xlabel('time (s)'), title(' predicted','FontSize',16) + spm_axis tight, drawnow + + +end + + +% inverse spectral responses for multiple levels of the parameter +%========================================================================== + +% setup model (DCM structure +%-------------------------------------------------------------------------- +options.DATA = 0; +DCM.A = A; +DCM.B = B; +DCM.C = C; +DCM.options = options; +DCM.M.dipfit = M.dipfit; +DCM.M.p = M.p; +DCM.M.dt = U.dt; + +DCM.xU = U; + +DCM.xY.Hz = M.Hz; +DCM.xY.dt = U.dt; +DCM.xY.pst = pst*1000; + +% invert +%-------------------------------------------------------------------------- +for i = 1:length(csd); + DCM.xY.y = csd(i); + CSD{i,1} = DCM; +end + +CSD = spm_dcm_fit(CSD); + +% Parametric empirical Bayes +%========================================================================== +% first identify the most likely parameter explaining changes. This +% involves comparing models defined in terms of the relative within and +% between trial variance. In other words, isolate a single parameter by +% setting the precision of between trial variance of the remaining +% parameters to a relatively small value. The free energy of the +% empirical Bayes model can then be used to evaluate which parameter +% estimates are likely to be responsible for between trial variations. +%-------------------------------------------------------------------------- +clear M; +M.hE = 0; +M.hC = 1/16; +M.bE = spm_vec(CSD{1}.M.pE); +M.bC = diag(spm_vec(CSD{1}.M.pC)); + + +% Define model space in terms of parameters allowed to vary +%-------------------------------------------------------------------------- +field = {'T(1)','T(2)','T(3)','G(1)','G(2)','G(3)'}; +beta = 32; +for i = 1:length(field) + + % restrict between trial precision (beta) + %---------------------------------------------------------------------- + j = spm_find_pC(CSD{1},field{i}); + M.pC = M.bC/beta; + M.pC(j,j) = M.bC(j,j); + + % invert and record free energy + %---------------------------------------------------------------------- + PEB = spm_dcm_peb(CSD,M,'all'); + F(i,1) = PEB.F; + +end + +% .results in terms of model comparison +%-------------------------------------------------------------------------- +spm_figure('GetWin','empirical Bayesian results');clf + +subplot(2,1,1) +bar(spm_softmax(F));set(gca,'XtickLabel',field) +xlabel('model'), ylabel('probability') +title('model comparison','FontSize',16), axis square + + +% recover posterior density and the selected model +%-------------------------------------------------------------------------- +j = spm_find_pC(CSD{1},field{end}); +M.pC = M.bC/beta; +M.pC(j,j) = M.bC(j,j); +[PEB,DCM] = spm_dcm_peb(CSD,M,'all'); + +for i = 1:length(csd); + q = spm_vec(DCM{i}.Ep); qE(i,1) = q(j); + q = diag( DCM{i}.Cp); qC(i,1) = q(j); +end + +subplot(2,1,2) +spm_plot_ci(qE,qC), hold on, bar(G,1/4), hold off +xlabel('trial'), ylabel('parameter estimate') +title('true and estimated changes','FontSize',16), axis square + + +return + + diff --git a/toolbox/OldNorm/spm_brainwarp.mexa64 b/toolbox/OldNorm/spm_brainwarp.mexa64 index 155492ff..3ace7193 100755 Binary files a/toolbox/OldNorm/spm_brainwarp.mexa64 and b/toolbox/OldNorm/spm_brainwarp.mexa64 differ diff --git a/toolbox/OldNorm/spm_brainwarp.mexmaci64 b/toolbox/OldNorm/spm_brainwarp.mexmaci64 index 91c6ae75..bad4008f 100755 Binary files a/toolbox/OldNorm/spm_brainwarp.mexmaci64 and b/toolbox/OldNorm/spm_brainwarp.mexmaci64 differ diff --git a/toolbox/OldNorm/spm_brainwarp.mexw32 b/toolbox/OldNorm/spm_brainwarp.mexw32 index fd0f7a0b..099acb5e 100755 Binary files a/toolbox/OldNorm/spm_brainwarp.mexw32 and b/toolbox/OldNorm/spm_brainwarp.mexw32 differ diff --git a/toolbox/OldNorm/spm_brainwarp.mexw64 b/toolbox/OldNorm/spm_brainwarp.mexw64 index 34d5e4d4..f525e142 100755 Binary files a/toolbox/OldNorm/spm_brainwarp.mexw64 and b/toolbox/OldNorm/spm_brainwarp.mexw64 differ diff --git a/toolbox/SPEM_and_DCM/spm_SEM_demo.m b/toolbox/SPEM_and_DCM/spm_SEM_demo.m index 8966e66a..db1dcb5c 100644 --- a/toolbox/SPEM_and_DCM/spm_SEM_demo.m +++ b/toolbox/SPEM_and_DCM/spm_SEM_demo.m @@ -5,7 +5,7 @@ % different experimental conditions. In what follows, we load the data % structure and assemble the slow (pursuit) eye movement trajectories that % will be subsequently fitted using a (Bayes optimal) model of oculomotor -% control. This is an example of meta-modelling � in the sense that the +% control. This is an example of meta-modelling - in the sense that the % Bayesian modelling of empirical eye tracking assumes that the behaviour % remodelled was generated by a Bayesian observer. There are four % conditions (slow and fast target velocity under smooth and noisy target @@ -25,7 +25,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_SEM_demo.m 6014 2014-05-23 15:00:35Z guillaume $ +% $Id: spm_SEM_demo.m 6501 2015-07-17 14:32:09Z spm $ % load and create data structure diff --git a/toolbox/Shoot/spm_shoot_defaults.m b/toolbox/Shoot/spm_shoot_defaults.m index d2b8f3e6..1f7b1bee 100644 --- a/toolbox/Shoot/spm_shoot_defaults.m +++ b/toolbox/Shoot/spm_shoot_defaults.m @@ -10,7 +10,7 @@ % Copyright (C) Wellcome Trust Centre for Neuroimaging (2009) % John Ashburner -% $Id: spm_shoot_defaults.m 5812 2013-12-20 17:52:07Z john $ +% $Id: spm_shoot_defaults.m 6489 2015-06-26 11:46:29Z john $ %_______________________________________________________________________ @@ -38,7 +38,7 @@ d.rparam = [1e-4 0.001 0.2 0.05 0.2]; % Regularisation parameters for deformation d.sparam = [0.0001 0.08 0.8]; % Regularisation parameters for blurring d.smits = 16; % No. smoothing iterations - +%d.smits = 0; d.sparam = []; d.scale = 0.8; % Fraction of Gauss-Newton update step to use d.bs_args = [2 2 2 1 1 1]; % B-spline settings for interpolation diff --git a/toolbox/Shoot/spm_shoot_scalmom.m b/toolbox/Shoot/spm_shoot_scalmom.m index 682b68b3..d45d175a 100644 --- a/toolbox/Shoot/spm_shoot_scalmom.m +++ b/toolbox/Shoot/spm_shoot_scalmom.m @@ -7,18 +7,18 @@ % J. Marron, Michael Wiener, and Sarang Joshi. "Multivariate % statistical analysis of deformation momenta relating anatomical % shape to neuropsychological measures." Medical Image Computing -% and Computer-Assisted Intervention–MICCAI 2010 (2010): 529-537. +% and Computer-Assisted Intervention-MICCAI 2010 (2010): 529-537. % % Singh, Nikhil, Angela Wang, Preethi Sankaranarayanan, P. Fletcher, % and Sarang Joshi. "Genetic, Structural and Functional Imaging % Biomarkers for Early Detection of Conversion from MCI to AD." -% Medical Image Computing and Computer-Assisted Intervention–MICCAI +% Medical Image Computing and Computer-Assisted Intervention-MICCAI % 2012 (2012): 132-140. -%_______________________________________________________________________ -% Copyright (C) 2013 Wellcome Trust Centre for Neuroimaging +%__________________________________________________________________________ +% Copyright (C) 2013-2015 Wellcome Trust Centre for Neuroimaging % John Ashburner -% $Id: spm_shoot_scalmom.m 5485 2013-05-09 15:51:24Z john $ +% $Id: spm_shoot_scalmom.m 6501 2015-07-17 14:32:09Z spm $ Pt = strvcat(job.template); Nt = nifti(Pt); diff --git a/toolbox/Shoot/spm_shoot_template.m b/toolbox/Shoot/spm_shoot_template.m index 5b191b2f..7b3135b8 100644 --- a/toolbox/Shoot/spm_shoot_template.m +++ b/toolbox/Shoot/spm_shoot_template.m @@ -14,7 +14,7 @@ % Copyright (C) Wellcome Trust Centre for Neuroimaging (2009) % John Ashburner -% $Id: spm_shoot_template.m 5782 2013-12-05 16:11:14Z john $ +% $Id: spm_shoot_template.m 6489 2015-06-26 11:46:29Z john $ %_______________________________________________________________________ d = spm_shoot_defaults; @@ -154,7 +154,7 @@ NG.mat0 = NG.mat; vx = sqrt(sum(NG.mat(1:3,1:3).^2)); -if ~isempty(sparam) || smits==0, +if ~isempty(sparam) && smits~=0, g0 = spm_shoot_blur(t,[vx, prod(vx)*[sparam(1:2) sched(1)*sparam(3)]],smits); for j=1:n1+1, g{j} = max(g0(:,:,:,j),1e-4); @@ -237,7 +237,9 @@ if ok(i) % Load velocity, mean adjust and re-save u = squeeze(single(NU(i).dat(:,:,:,:,:))); - u = u - su; % Subtract mean + if isempty(sparam) || smits==0 + u = u - su; % Subtract mean (unless template is smoothed) + end NU(i).dat(:,:,:,:,:) = reshape(u,[dm 1 3]); % Generate inverse deformation and save @@ -285,7 +287,7 @@ %%%%%%%%%%%%%%% % Re-generate template data from sufficient statistics - if ~isempty(sparam) || smits==0, + if ~isempty(sparam) && smits~=0, g0 = reconv(g,bs_args); g0 = spm_shoot_blur(t,[vx, prod(vx)*[sparam(1:2) sched(it+1)*sparam(3)]],smits,g0); g = cell(n1+1,1); diff --git a/toolbox/dcm_meeg/spm_api_erp.fig b/toolbox/dcm_meeg/spm_api_erp.fig index 656e4d35..bf311c37 100644 Binary files a/toolbox/dcm_meeg/spm_api_erp.fig and b/toolbox/dcm_meeg/spm_api_erp.fig differ diff --git a/toolbox/dcm_meeg/spm_api_erp.m b/toolbox/dcm_meeg/spm_api_erp.m index 5f674ff4..4057f9d9 100644 --- a/toolbox/dcm_meeg/spm_api_erp.m +++ b/toolbox/dcm_meeg/spm_api_erp.m @@ -6,7 +6,7 @@ % Copyright (C) 2005-2014 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_api_erp.m 6305 2015-01-17 12:40:51Z karl $ +% $Id: spm_api_erp.m 6644 2015-12-12 14:53:37Z karl $ %-Launch GUI @@ -84,6 +84,7 @@ function load_Callback(hObject, eventdata, handles, varargin) % 'TFM' - Time-frequency responses % 'IND' - Induced responses % 'PHA' - (for phase coupling) +% 'NFM' - Neural field model try model = DCM.options.analysis; @@ -146,6 +147,7 @@ function load_Callback(hObject, eventdata, handles, varargin) % 'ECD' - Equivalent current dipole % 'IMG' - Imaging % 'LFP' - Local field potentials +% 'ITR' - Intra-Laminar recording try model = DCM.options.spatial; catch @@ -159,6 +161,7 @@ function load_Callback(hObject, eventdata, handles, varargin) case{'IMG'}, set(handles.Spatial,'Value',1); case{'ECD'}, set(handles.Spatial,'Value',1); case{'LFP'}, set(handles.Spatial,'Value',2); + case{'ITR'}, set(handles.Spatial,'Value',4); otherwise end elseif ismember(DCM.options.analysis, {'NFM'}) @@ -171,6 +174,7 @@ function load_Callback(hObject, eventdata, handles, varargin) case{'IMG'}, set(handles.Spatial,'Value',1); case{'ECD'}, set(handles.Spatial,'Value',2); case{'LFP'}, set(handles.Spatial,'Value',3); + case{'ITR'}, set(handles.Spatial,'Value',4); otherwise end end @@ -437,7 +441,11 @@ function Datafile_Callback(hObject, eventdata, handles) if isequal(handles.DCM.xY.modality, 'LFP') set(handles.Spatial, 'Value', find(strcmp('LFP', get(handles.Spatial, 'String')))); -end +end + +if isequal(handles.DCM.xY.modality, 'ILAM') + set(handles.Spatial, 'Value', find(strcmp('ITR', get(handles.Spatial, 'String')))); +end % Assemble and display data %-------------------------------------------------------------------------- @@ -656,10 +664,13 @@ function design_Callback(hObject, eventdata, handles) % forward model (spatial) %------------------------------------------------------------------ - DCM = spm_dcm_erp_dipfit(DCM); + try + DCM = spm_dcm_erp_dipfit(DCM); + end set(handles.plot_dipoles,'enable','on') + - case{'LFP'} + case{'LFP','ITR'} if ~isdeployed, addpath(fullfile(spm('Dir'),'toolbox','Neural_Models')); @@ -1285,7 +1296,7 @@ function BMS_Callback(hObject, eventdata, handles) set(handles.text20, 'String', 'modes'); set(handles.model, 'Enable','on'); - set(handles.Spatial, 'String',{'IMG','ECD','LFP'}); + set(handles.Spatial, 'String',{'IMG','ECD','LFP','ITR'}); set(handles.Wavelet, 'Enable','off'); set(handles.onset, 'Enable','on'); set(handles.dur, 'Enable','on'); @@ -1316,7 +1327,7 @@ function BMS_Callback(hObject, eventdata, handles) set(handles.text20, 'String', 'modes'); set(handles.model, 'Enable','on'); - set(handles.Spatial,'String',{'IMG','ECD','LFP'}); + set(handles.Spatial,'String',{'IMG','ECD','LFP','ITR'}); set(handles.Wavelet,'Enable','on'); set(handles.onset, 'Enable','off'); set(handles.dur, 'Enable','off'); @@ -1348,7 +1359,7 @@ function BMS_Callback(hObject, eventdata, handles) end set(handles.text20, 'String','modes'); - set(handles.Spatial,'String',{'IMG','ECD','LFP'}); + set(handles.Spatial,'String',{'IMG','ECD','LFP','ITR'}); set(handles.Wavelet,'Enable','on','String','Spectral density'); set(handles.onset, 'Enable','on'); set(handles.dur, 'Enable','on'); diff --git a/toolbox/dcm_meeg/spm_csd_mtf.m b/toolbox/dcm_meeg/spm_csd_mtf.m index c3b8f4d0..c4a50129 100644 --- a/toolbox/dcm_meeg/spm_csd_mtf.m +++ b/toolbox/dcm_meeg/spm_csd_mtf.m @@ -35,7 +35,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_csd_mtf.m 6112 2014-07-21 09:39:53Z karl $ +% $Id: spm_csd_mtf.m 6481 2015-06-16 17:01:47Z karl $ @@ -146,6 +146,22 @@ y = g; end +% model data features summarised with a MAR process +%========================================================================== +if isfield(M,'p') + p = M.p - 1; + if isfield(M,'dt') + dt = M.dt; + else + dt = 1/(2*w(end)); + end + for c = 1:length(y) + mar = spm_csd2mar(y{c},w,p,dt); + y{c} = spm_mar2csd(mar,w,1/dt); + end +end + + % Granger causality (normalised transfer functions) if requested %========================================================================== if nargout > 3 diff --git a/toolbox/dcm_meeg/spm_dcm_csd.m b/toolbox/dcm_meeg/spm_dcm_csd.m index fb3c5360..f2bf35e4 100644 --- a/toolbox/dcm_meeg/spm_dcm_csd.m +++ b/toolbox/dcm_meeg/spm_dcm_csd.m @@ -41,7 +41,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_csd.m 5975 2014-05-07 18:07:42Z karl $ +% $Id: spm_dcm_csd.m 6481 2015-06-16 17:01:47Z karl $ % check options @@ -90,7 +90,7 @@ % check to see if neuronal priors have already been specified %-------------------------------------------------------------------------- try - if length(spm_vec(DCM.M.pE)) == length(spm_vec(pE)); + if spm_length(DCM.M.pE) == spm_length(pE); pE = DCM.M.pE; pC = DCM.M.pC; fprintf('Using existing priors\n') @@ -104,6 +104,14 @@ % augment with priors on endogenous inputs (neuronal) and noise %-------------------------------------------------------------------------- [pE,pC] = spm_ssr_priors(pE,pC); + +try + if spm_length(DCM.M.pE) == spm_length(pE); + pE = DCM.M.pE; + pC = DCM.M.pC; + fprintf('Using existing priors\n') + end +end % initial states and equations of motion %-------------------------------------------------------------------------- diff --git a/toolbox/dcm_meeg/spm_dcm_csd_data.m b/toolbox/dcm_meeg/spm_dcm_csd_data.m index b1b50b20..1c235d54 100644 --- a/toolbox/dcm_meeg/spm_dcm_csd_data.m +++ b/toolbox/dcm_meeg/spm_dcm_csd_data.m @@ -26,7 +26,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_csd_data.m 6434 2015-05-10 20:16:28Z vladimir $ +% $Id: spm_dcm_csd_data.m 6481 2015-06-16 17:01:47Z karl $ % Set defaults and Get D filename %------------------------------------------------------------------------- @@ -181,7 +181,7 @@ DCM.xY.csd = cell(1,Ne); % CSD for each condition w = min(fix(2/DCM.xY.dt),Nb); % window length (bins) -m = 1; % retain primcipal mode +m = 1; % retain principal mode for i = 1:Ne; % trial indices diff --git a/toolbox/dcm_meeg/spm_dcm_erp.m b/toolbox/dcm_meeg/spm_dcm_erp.m index 5059e04c..18ece5f9 100644 --- a/toolbox/dcm_meeg/spm_dcm_erp.m +++ b/toolbox/dcm_meeg/spm_dcm_erp.m @@ -36,7 +36,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_erp.m 6427 2015-05-05 15:42:35Z karl $ +% $Id: spm_dcm_erp.m 6645 2015-12-12 14:55:22Z karl $ % check options (and clear persistent variables) %========================================================================== @@ -53,7 +53,7 @@ try, Nm = DCM.options.Nmodes; catch, Nm = 8; end try, onset = DCM.options.onset; catch, onset = 60; end try, dur = DCM.options.dur; catch, dur = 16; end -try, model = DCM.options.model; catch, model = 'NMM'; end +try, model = DCM.options.model; catch, model = 'CMC'; end try, lock = DCM.options.lock; catch, lock = 0; end try, multC = DCM.options.multiC; catch, multC = 0; end try, symm = DCM.options.symmetry; catch, symm = 0; end @@ -78,6 +78,9 @@ xU = DCM.xU; M = DCM.M; +if ~isfield(xY,'X0'), xY.X0 = sparse(size(xY.y{1},1),0); end +if ~isfield(xU,'X'), xU.X = sparse(1,0); end +if ~isfield(xY,'scale'), xY.scale = 1; end % dimensions %-------------------------------------------------------------------------- @@ -401,5 +404,6 @@ % and save %-------------------------------------------------------------------------- -save(DCM.name, 'DCM', spm_get_defaults('mat.format')); - +if DATA + save(DCM.name, 'DCM', spm_get_defaults('mat.format')); +end diff --git a/toolbox/dcm_meeg/spm_dcm_erp_results.m b/toolbox/dcm_meeg/spm_dcm_erp_results.m index 5c8dc26a..6a6c8d5c 100644 --- a/toolbox/dcm_meeg/spm_dcm_erp_results.m +++ b/toolbox/dcm_meeg/spm_dcm_erp_results.m @@ -30,7 +30,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dcm_erp_results.m 6112 2014-07-21 09:39:53Z karl $ +% $Id: spm_dcm_erp_results.m 6644 2015-12-12 14:53:37Z karl $ % get Action if necessary @@ -181,6 +181,7 @@ % spm_dcm_erp_results(DCM,'ERPs (sources)'); %------------------------------------------------------------------ col = {'b','r','g','m','y','c'}; A = [0 0]; + sty = {':','-.','-','--','-'}; for i = 1:ns str = {}; subplot(ceil(ns/2),2,i), hold on @@ -204,15 +205,12 @@ %---------------------------------------------------------- for j = 1:np for k = 1:nt - if j == np + plot(t, DCM.K{k}(:,i + ns*(j - 1)), ... 'Color',col{k}, ... + 'LineStyle',sty{j}, ... 'LineWidth',2); - else - plot(t, DCM.K{k}(:,i + ns*(j - 1)), ':', ... - 'Color',col{k}, ... - 'LineWidth',2); - end + str{end + 1} = sprintf('trial %i (pop. %i)',k,j); end end diff --git a/toolbox/dcm_meeg/spm_erp_L.m b/toolbox/dcm_meeg/spm_erp_L.m index 19e31a73..ed66d35b 100644 --- a/toolbox/dcm_meeg/spm_erp_L.m +++ b/toolbox/dcm_meeg/spm_erp_L.m @@ -25,7 +25,7 @@ % Copyright (C) 2005 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_erp_L.m 5775 2013-12-04 13:03:55Z vladimir $ +% $Id: spm_erp_L.m 6475 2015-06-06 10:44:20Z karl $ % Create a persient variable that rembers the last locations %-------------------------------------------------------------------------- @@ -110,6 +110,12 @@ n = m; end L = sparse(1:m,1:m,P.L,m,n); + + % assume common sources contribute to the last channel + %------------------------------------------------------------------ + if isfield(dipfit,'common_source') + L(m,m:n) = L(m,m); + end otherwise warndlg('unknown spatial model') diff --git a/toolbox/dcm_meeg/spm_fs_csd.m b/toolbox/dcm_meeg/spm_fs_csd.m index 8b37f7fc..a8e92aa2 100644 --- a/toolbox/dcm_meeg/spm_fs_csd.m +++ b/toolbox/dcm_meeg/spm_fs_csd.m @@ -5,20 +5,20 @@ % M - model structure %__________________________________________________________________________ % -% This simply log-transforms the (real) auto-spectra -% % David O, Friston KJ (2003) A neural mass model for MEG/EEG: coupling and % neuronal dynamics. NeuroImage 20: 1743-1755 %__________________________________________________________________________ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_fs_csd.m 5895 2014-02-26 14:28:23Z karl $ +% $Id: spm_fs_csd.m 6557 2015-09-20 12:44:30Z karl $ % return (scaled) cross-spectra and covariance functions %-------------------------------------------------------------------------- for i = 1:length(y); - ccf = spm_csd2ccf(y{i},M.Hz); - y{i} = [y{i}*8; ccf(1:8:end,:,:)]; + csd = y{i}; + ccf = spm_csd2ccf(csd,M.Hz); + y{i} = [csd*8; ccf(1:8:end,:,:)]; + % y{i} = [log(csd); ccf(1:8:end,:,:)]; end diff --git a/toolbox/mci/Contents.m b/toolbox/mci/Contents.m new file mode 100644 index 00000000..207e3588 --- /dev/null +++ b/toolbox/mci/Contents.m @@ -0,0 +1,87 @@ +% +% MONTE CARLO INFERENCE (MCI) toolbox +% +% INFERENCE FOR SINGLE DATA SET +% +% spm_mci_lgv.m Langevin Monte Carlo (LMC) otherwise known as Simplified +% Manifold Metropolis Adjusted Langevin Algorithm +% (Simplified MMALA). +% +% spm_mci_ais.m Annealed Importance Sampling (AIS) +% +% spm_mci_pop.m Adaptive Monte Carlo (AMC): Metropolis-Hastings with +% proposals tuned using Robbins-Monro. Also allows for +% multiple chains and thermodynamic integration +% +% spm_mci_post.m Generic wrapper for single subject Bayesian inference. +% Implemented using LMC, AIS, AMC or Variational Laplace (VL) +% +% mci_demo_approach.m Approach to limit example +% mci_demo_discount.m Temporal discounting model +% mci_demo_growth.m Preece-Baines growth model +% mci_demo_lds.m Linear dynamical system +% mci_demo_linear.m Linear regression +% mci_demo_linsqr.m (Squared) Linear regression with local minima +% mci_demo_logistic.m Logistic regression +% mci_demo_nmm.m Neural mass models +% mci_demo_phase.m Fully connected phase coupling models +% mci_demo_rphase.m Phase coupling models with specific connectivity +% mci_demo_ramsay.m Nonlinear oscillator with local minima +% +% When setting up inference for a new dynamical model, use spm_mci_check(M) to +% see that model M has required fields. +% +% INFERENCE FOR GROUP DATA +% +% spm_mci_mfx.m Mixed effects inference for nonlinear systems +% spm_mci_mfx_dynamic.m Mixed effects inference for dynamical systems +% +% mci_demo_rfx_linear.m Random effects linear regression and comparison +% with parametric Empirical Bayes +% mci_demo_rfx_logistic.m Random effects logistic regression +% mci_demo_rfx_nmm.m Random effects neural mass models +% mci_demo_rfx_rphase.m Random effects phase coupling +% mci_demo_mfx_lds.m Mixed effects linear dynamical systems +% +% INTEGRATION +% +% spm_mci_fwd.m Integrate dynamics and apply observation model. +% spm_mci_sens.m Forward Sensitivity analysis +% spm_mci_adjoint.m Adjoint Sensitivity analysis +% +% mci_compare_forward.m Compare integration speed of various methods +% mci_compare_gradients.m Compare accuracy of gradient estimation +% mci_compare_sensitivities Compare speed of sensitivity estimation +% +% The sensitivity matrices are more efficiently computed if you have +% installed the four major components of the Sundials package (CVODE, +% CVODES,IDA,IDAS) from http://computation.llnl.gov/casc/sundials/ +% +% DIAGNOSTICS +% +% spm_mci_diag.m Trace plots, energy trajectory +% spm_mci_ess.m Effective sample size for a Markov chain +% spm_mci_stat.m Test for stationarity +% spm_mci_quantiles Histograms and quantiles from samples +% +% REFERENCES: +% +% W.Penny, M Klein-Flugge and B Sengupta (2015) Mixed-Effects Langevin +% Monte Carlo, Submitted, 2015. +% +% W.Penny and B Sengupta (2015) Annealed Importance Sampling for Neural +% Mass Models, Submitted, 2015. +% +% B. Sengupta, K. Friston and W. Penny (2015) Gradient-based MCMC samplers +% for dynamic causal modelling. Neuroimage. +% +% B. Sengupta, K. Friston and W. Penny (2015) Gradient-free MCMC samplers +% for dynamic causal modelling. Neuroimage, 112, 375-381. +% +% B. Sengupta, K. Friston and W. Penny (2014) Efficient Gradient +% Computation for Dynamical Models. Neuroimage,98, 521-527. +%_________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: Contents.m 6548 2015-09-11 12:39:47Z will $ \ No newline at end of file diff --git a/toolbox/mci/data/README b/toolbox/mci/data/README new file mode 100644 index 00000000..a6fd73e0 --- /dev/null +++ b/toolbox/mci/data/README @@ -0,0 +1,47 @@ +Datasets for `Pattern Recognition and Neural Networks' by B.D. Ripley +===================================================================== + +Cambridge University Press (1996) ISBN 0-521-46086-7 + +The background to the datasets is described in section 1.4; this file +relates the computer-readable files to that description. + + +synthetic two-class problem +--------------------------- + +Data from Ripley (1994a). + +This has two real-valued co-ordinates (xs and ys) and a class (xc) +which is 0 or 1. + +Data file synth.tr has 250 rows of the training set + synth.te has 1000 rows of the test set (not used here) + + + +Diabetes in Pima Indians +------------------------ + +A population of women who were at least 21 years old, of Pima Indian heritage +and living near Phoenix, Arizona, was tested for diabetes +according to World Health Organization criteria. The data +were collected by the US National Institute of Diabetes and Digestive and +Kidney Diseases (Smith et al, 1988). This example is also contained in the +UCI machine-learning database collection (Murphy & Aha, 1995). + +The data files have rows containing + +npreg number of pregnancies +glu plasma glucose concentration in an oral glucose tolerance test +bp diastolic blood pressure (mm Hg) +skin triceps skin fold thickness (mm) +ins serum insulin (micro U/ml) +bmi body mass index (weight in kg/(height in m)^2) +ped diabetes pedigree function +age in years +type No / Yes + +Data file pima.tr has 200 rows of complete training data. + pima.te has 332 rows of complete test data. + diff --git a/toolbox/mci/data/pima_te.txt b/toolbox/mci/data/pima_te.txt new file mode 100644 index 00000000..850c344b --- /dev/null +++ b/toolbox/mci/data/pima_te.txt @@ -0,0 +1,332 @@ + 6 148 72 35 33.6 0.627 50 1 + 1 85 66 29 26.6 0.351 31 0 + 1 89 66 23 28.1 0.167 21 0 + 3 78 50 32 31.0 0.248 26 1 + 2 197 70 45 30.5 0.158 53 1 + 5 166 72 19 25.8 0.587 51 1 + 0 118 84 47 45.8 0.551 31 1 + 1 103 30 38 43.3 0.183 33 0 + 3 126 88 41 39.3 0.704 27 0 + 9 119 80 35 29.0 0.263 29 1 + 1 97 66 15 23.2 0.487 22 0 + 5 109 75 26 36.0 0.546 60 0 + 3 88 58 11 24.8 0.267 22 0 +10 122 78 31 27.6 0.512 45 0 + 4 103 60 33 24.0 0.966 33 0 + 9 102 76 37 32.9 0.665 46 1 + 2 90 68 42 38.2 0.503 27 1 + 4 111 72 47 37.1 1.390 56 1 + 3 180 64 25 34.0 0.271 26 0 + 7 106 92 18 22.7 0.235 48 0 + 9 171 110 24 45.4 0.721 54 1 + 0 180 66 39 42.0 1.893 25 1 + 2 71 70 27 28.0 0.586 22 0 + 1 103 80 11 19.4 0.491 22 0 + 1 101 50 15 24.2 0.526 26 0 + 5 88 66 21 24.4 0.342 30 0 + 7 150 66 42 34.7 0.718 42 0 + 1 73 50 10 23.0 0.248 21 0 + 0 105 64 41 41.5 0.173 22 0 + 5 99 74 27 29.0 0.203 32 0 + 0 109 88 30 32.5 0.855 38 1 + 1 95 66 13 19.6 0.334 25 0 + 4 146 85 27 28.9 0.189 27 0 + 2 100 66 20 32.9 0.867 28 1 + 4 129 86 20 35.1 0.231 23 0 + 5 95 72 33 37.7 0.370 27 0 + 2 112 66 22 25.0 0.307 24 0 + 3 113 44 13 22.4 0.140 22 0 + 7 83 78 26 29.3 0.767 36 0 + 0 101 65 28 24.6 0.237 22 0 +13 106 72 54 36.6 0.178 45 0 + 2 100 68 25 38.5 0.324 26 0 +15 136 70 32 37.1 0.153 43 1 + 4 123 80 15 32.0 0.443 34 0 + 7 81 78 40 46.7 0.261 42 0 + 2 92 62 28 31.6 0.130 24 0 + 6 93 50 30 28.7 0.356 23 0 + 1 122 90 51 49.7 0.325 31 1 + 1 81 72 18 26.6 0.283 24 0 + 1 126 56 29 28.7 0.801 21 0 + 4 144 58 28 29.5 0.287 37 0 + 1 89 76 34 31.2 0.192 23 0 + 7 160 54 32 30.5 0.588 39 1 + 4 97 60 23 28.2 0.443 22 0 + 0 162 76 56 53.2 0.759 25 1 + 2 107 74 30 33.6 0.404 23 0 + 1 88 30 42 55.0 0.496 26 1 + 1 117 88 24 34.5 0.403 40 1 + 4 173 70 14 29.7 0.361 33 1 + 3 170 64 37 34.5 0.356 30 1 + 8 84 74 31 38.3 0.457 39 0 + 0 100 70 26 30.8 0.597 21 0 + 0 93 60 25 28.7 0.532 22 0 + 5 106 82 30 39.5 0.286 38 0 + 2 108 52 26 32.5 0.318 22 0 + 2 106 64 35 30.5 1.400 34 0 + 2 90 70 17 27.3 0.085 22 0 + 9 156 86 28 34.3 1.189 42 1 + 1 153 82 42 40.6 0.687 23 0 + 7 152 88 44 50.0 0.337 36 1 + 2 88 74 19 29.0 0.229 22 0 +17 163 72 41 40.9 0.817 47 1 + 4 151 90 38 29.7 0.294 36 0 + 7 102 74 40 37.2 0.204 45 0 + 0 114 80 34 44.2 0.167 27 0 + 6 104 74 18 29.9 0.722 41 1 + 2 75 64 24 29.7 0.370 33 0 + 8 179 72 42 32.7 0.719 36 1 + 0 129 110 46 67.1 0.319 26 1 + 1 128 98 41 32.0 1.321 33 1 + 8 109 76 39 27.9 0.640 31 1 + 4 109 64 44 34.8 0.905 26 1 + 0 113 80 16 31.0 0.874 21 0 + 0 108 68 20 27.3 0.787 32 0 + 5 111 72 28 23.9 0.407 27 0 + 8 196 76 29 37.5 0.605 57 1 + 2 81 60 22 27.7 0.290 25 0 + 0 147 85 54 42.8 0.375 24 0 + 5 109 62 41 35.8 0.514 25 1 + 6 125 68 30 30.0 0.464 32 0 + 5 85 74 22 29.0 1.224 32 1 + 7 142 60 33 28.8 0.687 61 0 + 1 100 66 15 23.6 0.666 26 0 + 1 87 78 27 34.6 0.101 22 0 + 3 162 52 38 37.2 0.652 24 1 + 4 197 70 39 36.7 2.329 31 0 + 0 117 80 31 45.2 0.089 24 0 + 6 134 80 37 46.2 0.238 46 1 + 3 74 68 28 29.7 0.293 23 0 + 7 181 84 21 35.9 0.586 51 1 + 0 179 90 27 44.1 0.686 23 1 + 1 91 64 24 29.2 0.192 21 0 + 4 91 70 32 33.1 0.446 22 0 + 6 119 50 22 27.1 1.318 33 1 + 2 146 76 35 38.2 0.329 29 0 + 9 184 85 15 30.0 1.213 49 1 + 0 165 90 33 52.3 0.427 23 0 + 9 124 70 33 35.4 0.282 34 0 + 1 111 86 19 30.1 0.143 23 0 + 2 90 80 14 24.4 0.249 24 0 + 1 113 64 35 33.6 0.543 21 1 + 3 111 56 39 30.1 0.557 30 0 +11 155 76 28 33.3 1.353 51 1 + 4 95 70 32 32.1 0.612 24 0 + 5 96 74 18 33.6 0.997 43 0 + 2 128 64 42 40.0 1.101 24 0 +10 101 86 37 45.6 1.136 38 1 + 2 108 62 32 25.2 0.128 21 0 + 2 100 70 52 40.5 0.677 25 0 + 7 106 60 24 26.5 0.296 29 1 + 0 104 64 23 27.8 0.454 23 0 + 2 108 62 10 25.3 0.881 22 0 + 7 133 88 15 32.4 0.262 37 0 + 7 136 74 26 26.0 0.647 51 0 + 1 119 86 39 45.6 0.808 29 1 + 4 96 56 17 20.8 0.340 26 0 + 0 78 88 29 36.9 0.434 21 0 + 0 107 62 30 36.6 0.757 25 1 + 6 151 62 31 35.5 0.692 28 0 + 2 146 70 38 28.0 0.337 29 1 + 0 126 84 29 30.7 0.520 24 0 + 2 144 58 33 31.6 0.422 25 1 + 2 120 76 37 39.7 0.215 29 0 +10 161 68 23 25.5 0.326 47 1 + 0 128 68 19 30.5 1.391 25 1 + 2 124 68 28 32.9 0.875 30 1 + 2 155 74 17 26.6 0.433 27 1 + 3 113 50 10 29.5 0.626 25 0 + 7 109 80 31 35.9 1.127 43 1 + 3 115 66 39 38.1 0.150 28 0 +13 152 90 33 26.8 0.731 43 1 + 2 112 75 32 35.7 0.148 21 0 + 1 157 72 21 25.6 0.123 24 0 + 1 122 64 32 35.1 0.692 30 1 + 2 102 86 36 45.5 0.127 23 1 + 6 105 70 32 30.8 0.122 37 0 + 8 118 72 19 23.1 1.476 46 0 + 2 87 58 16 32.7 0.166 25 0 + 1 95 60 18 23.9 0.260 22 0 + 1 130 70 13 25.9 0.472 22 0 + 1 95 74 21 25.9 0.673 36 0 + 8 126 88 36 38.5 0.349 49 0 + 1 139 46 19 28.7 0.654 22 0 + 3 99 62 19 21.8 0.279 26 0 + 1 125 50 40 33.3 0.962 28 1 + 1 196 76 36 36.5 0.875 29 1 + 5 189 64 33 31.2 0.583 29 1 + 5 103 108 37 39.2 0.305 65 0 + 4 147 74 25 34.9 0.385 30 0 + 5 99 54 28 34.0 0.499 30 0 + 3 81 86 16 27.5 0.306 22 0 + 3 173 82 48 38.4 2.137 25 1 + 0 84 64 22 35.8 0.545 21 0 + 0 98 82 15 25.2 0.299 22 0 + 1 87 60 37 37.2 0.509 22 0 + 0 93 100 39 43.4 1.021 35 0 + 0 105 68 22 20.0 0.236 22 0 + 1 90 62 18 25.1 1.268 25 0 + 1 125 70 24 24.3 0.221 25 0 + 1 119 54 13 22.3 0.205 24 0 + 5 116 74 29 32.3 0.660 35 1 + 8 105 100 36 43.3 0.239 45 1 + 3 100 68 23 31.6 0.949 28 0 + 1 131 64 14 23.7 0.389 21 0 + 2 127 58 24 27.7 1.600 25 0 + 3 96 56 34 24.7 0.944 39 0 + 3 193 70 31 34.9 0.241 25 1 + 5 136 84 41 35.0 0.286 35 1 + 9 72 78 25 31.6 0.280 38 0 + 1 172 68 49 42.4 0.702 28 1 + 6 102 90 39 35.7 0.674 28 0 + 1 112 72 30 34.4 0.528 25 0 + 1 143 84 23 42.4 1.076 22 0 + 3 173 84 33 35.7 0.258 22 1 + 4 144 82 32 38.5 0.554 37 1 + 3 129 64 29 26.4 0.219 28 1 + 1 119 88 41 45.3 0.507 26 0 + 2 94 68 18 26.0 0.561 21 0 + 0 102 64 46 40.6 0.496 21 0 + 8 151 78 32 42.9 0.516 36 1 + 1 181 64 30 34.1 0.328 38 1 + 1 95 82 25 35.0 0.233 43 1 + 3 89 74 16 30.4 0.551 38 0 + 1 80 74 11 30.0 0.527 22 0 + 1 90 68 8 24.5 1.138 36 0 + 0 189 104 25 34.3 0.435 41 1 + 4 117 64 27 33.2 0.230 24 0 + 0 180 78 63 59.4 2.420 25 1 + 0 104 64 37 33.6 0.510 22 1 + 0 120 74 18 30.5 0.285 26 0 + 1 82 64 13 21.2 0.415 23 0 + 0 91 68 32 39.9 0.381 25 0 + 9 134 74 33 25.9 0.460 81 0 + 9 120 72 22 20.8 0.733 48 0 + 8 74 70 40 35.3 0.705 39 0 + 5 88 78 30 27.6 0.258 37 0 + 0 124 56 13 21.8 0.452 21 0 + 0 97 64 36 36.8 0.600 25 0 + 1 144 82 40 41.3 0.607 28 0 + 0 137 70 38 33.2 0.170 22 0 + 4 132 86 31 28.0 0.419 63 0 + 3 158 70 30 35.5 0.344 35 1 + 0 123 88 37 35.2 0.197 29 0 + 0 84 82 31 38.2 0.233 23 0 + 0 135 68 42 42.3 0.365 24 1 + 1 139 62 41 40.7 0.536 21 0 + 0 173 78 32 46.5 1.159 58 0 + 2 83 65 28 36.8 0.629 24 0 + 2 89 90 30 33.5 0.292 42 0 + 4 99 68 38 32.8 0.145 33 0 + 4 125 70 18 28.9 1.144 45 1 + 2 81 72 15 30.1 0.547 25 0 + 6 154 74 32 29.3 0.839 39 0 + 2 117 90 19 25.2 0.313 21 0 + 3 84 72 32 37.2 0.267 28 0 + 7 94 64 25 33.3 0.738 41 0 + 3 96 78 39 37.3 0.238 40 0 +12 84 72 31 29.7 0.297 46 1 + 3 99 54 19 25.6 0.154 24 0 + 3 163 70 18 31.6 0.268 28 1 + 9 145 88 34 30.3 0.771 53 1 + 6 129 90 7 19.6 0.582 60 0 + 2 68 70 32 25.0 0.187 25 0 + 3 87 60 18 21.8 0.444 21 0 + 2 122 60 18 29.8 0.717 22 0 + 1 77 56 30 33.3 1.251 24 0 + 0 127 80 37 36.3 0.804 23 0 + 3 128 72 25 32.4 0.549 27 1 +10 90 85 32 34.9 0.825 56 1 + 4 84 90 23 39.5 0.159 25 0 + 1 88 78 29 32.0 0.365 29 0 + 8 186 90 35 34.5 0.423 37 1 + 5 187 76 27 43.6 1.034 53 1 + 4 131 68 21 33.1 0.160 28 0 + 1 116 70 28 27.4 0.204 21 0 + 3 84 68 30 31.9 0.591 25 0 + 1 88 62 24 29.9 0.422 23 0 + 1 84 64 23 36.9 0.471 28 0 +11 103 68 40 46.2 0.126 42 0 + 6 99 60 19 26.9 0.497 32 0 + 1 99 72 30 38.6 0.412 21 0 + 3 111 58 31 29.5 0.430 22 0 + 2 98 60 17 34.7 0.198 22 0 + 1 143 86 30 30.1 0.892 23 0 + 1 119 44 47 35.5 0.280 25 0 + 6 108 44 20 24.0 0.813 35 0 + 3 176 86 27 33.3 1.154 52 1 +11 111 84 40 46.8 0.925 45 1 + 2 112 78 50 39.4 0.175 24 0 + 2 82 52 22 28.5 1.699 25 0 + 6 123 72 45 33.6 0.733 34 0 + 1 89 24 19 27.8 0.559 21 0 + 1 108 88 19 27.1 0.400 24 0 + 1 124 60 32 35.8 0.514 21 0 + 1 181 78 42 40.0 1.258 22 1 + 1 92 62 25 19.5 0.482 25 0 + 0 152 82 39 41.5 0.270 27 0 + 3 174 58 22 32.9 0.593 36 1 + 6 105 80 28 32.5 0.878 26 0 +11 138 74 26 36.1 0.557 50 1 + 2 68 62 13 20.1 0.257 23 0 + 9 112 82 24 28.2 1.282 50 1 + 0 94 70 27 43.5 0.347 21 0 + 4 90 88 47 37.7 0.362 29 0 + 4 94 65 22 24.7 0.148 21 0 + 0 102 78 40 34.5 0.238 24 0 + 1 128 82 17 27.5 0.115 22 0 + 7 97 76 32 40.9 0.871 32 1 + 1 100 74 12 19.5 0.149 28 0 + 3 103 72 30 27.6 0.730 27 0 + 0 179 50 36 37.8 0.455 22 1 +11 136 84 35 28.3 0.260 42 1 + 1 117 60 23 33.8 0.466 27 0 + 2 155 52 27 38.7 0.240 25 1 + 2 101 58 35 21.8 0.155 22 0 + 1 112 80 45 34.8 0.217 24 0 + 4 145 82 18 32.5 0.235 70 1 +10 111 70 27 27.5 0.141 40 1 + 6 98 58 33 34.0 0.430 43 0 + 6 165 68 26 33.6 0.631 49 0 +10 68 106 23 35.5 0.285 47 0 + 3 123 100 35 57.3 0.880 22 0 + 0 162 76 36 49.6 0.364 26 1 + 0 95 64 39 44.6 0.366 22 0 + 2 129 74 26 33.2 0.591 25 0 + 1 107 50 19 28.3 0.181 29 0 + 7 142 90 24 30.4 0.128 43 1 + 3 169 74 19 29.9 0.268 31 1 + 6 80 80 36 39.8 0.177 28 0 + 2 127 46 21 34.4 0.176 22 0 + 2 93 64 32 38.0 0.674 23 1 + 5 126 78 27 29.6 0.439 40 0 +10 129 62 36 41.2 0.441 38 1 + 0 134 58 20 26.4 0.352 21 0 + 7 187 50 33 33.9 0.826 34 1 + 3 173 78 39 33.8 0.970 31 1 +10 94 72 18 23.1 0.595 56 0 + 1 108 60 46 35.5 0.415 24 0 + 5 117 86 30 39.1 0.251 42 0 + 1 116 78 29 36.1 0.496 25 0 + 0 141 84 26 32.4 0.433 22 0 + 2 174 88 37 44.5 0.646 24 1 + 2 106 56 27 29.0 0.426 22 0 + 0 126 86 27 27.4 0.515 21 0 + 8 65 72 23 32.0 0.600 42 0 + 2 99 60 17 36.6 0.453 21 0 +11 120 80 37 42.3 0.785 48 1 + 3 102 44 20 30.8 0.400 26 0 + 1 109 58 18 28.5 0.219 22 0 +13 153 88 37 40.6 1.174 39 0 +12 100 84 33 30.0 0.488 46 0 + 1 147 94 41 49.3 0.358 27 1 + 3 187 70 22 36.4 0.408 36 1 + 1 121 78 39 39.0 0.261 28 0 + 3 108 62 24 26.0 0.223 25 0 + 0 181 88 44 43.3 0.222 26 1 + 1 128 88 39 36.5 1.057 37 1 + 2 88 58 26 28.4 0.766 22 0 + 9 170 74 31 44.0 0.403 43 1 +10 101 76 48 32.9 0.171 63 0 + 5 121 72 23 26.2 0.245 30 0 + 1 93 70 31 30.4 0.315 23 0 diff --git a/toolbox/mci/data/pima_tr.txt b/toolbox/mci/data/pima_tr.txt new file mode 100644 index 00000000..453c0c3c --- /dev/null +++ b/toolbox/mci/data/pima_tr.txt @@ -0,0 +1,200 @@ + 5 86 68 28 30.2 0.364 24 0 + 7 195 70 33 25.1 0.163 55 1 + 5 77 82 41 35.8 0.156 35 0 + 0 165 76 43 47.9 0.259 26 0 + 0 107 60 25 26.4 0.133 23 0 + 5 97 76 27 35.6 0.378 52 1 + 3 83 58 31 34.3 0.336 25 0 + 1 193 50 16 25.9 0.655 24 0 + 3 142 80 15 32.4 0.200 63 0 + 2 128 78 37 43.3 1.224 31 1 + 0 137 40 35 43.1 2.288 33 1 + 9 154 78 30 30.9 0.164 45 0 + 1 189 60 23 30.1 0.398 59 1 +12 92 62 7 27.6 0.926 44 1 + 1 86 66 52 41.3 0.917 29 0 + 4 99 76 15 23.2 0.223 21 0 + 1 109 60 8 25.4 0.947 21 0 +11 143 94 33 36.6 0.254 51 1 + 1 149 68 29 29.3 0.349 42 1 + 0 139 62 17 22.1 0.207 21 0 + 2 99 70 16 20.4 0.235 27 0 + 1 100 66 29 32.0 0.444 42 0 + 4 83 86 19 29.3 0.317 34 0 + 0 101 64 17 21.0 0.252 21 0 + 1 87 68 34 37.6 0.401 24 0 + 9 164 84 21 30.8 0.831 32 1 + 1 99 58 10 25.4 0.551 21 0 + 0 140 65 26 42.6 0.431 24 1 + 5 108 72 43 36.1 0.263 33 0 + 2 110 74 29 32.4 0.698 27 0 + 1 79 60 42 43.5 0.678 23 0 + 3 148 66 25 32.5 0.256 22 0 + 0 121 66 30 34.3 0.203 33 1 + 3 158 64 13 31.2 0.295 24 0 + 2 105 80 45 33.7 0.711 29 1 +13 145 82 19 22.2 0.245 57 0 + 1 79 80 25 25.4 0.583 22 0 + 1 71 48 18 20.4 0.323 22 0 + 0 102 86 17 29.3 0.695 27 0 + 0 119 66 27 38.8 0.259 22 0 + 8 176 90 34 33.7 0.467 58 1 + 1 97 68 21 27.2 1.095 22 0 + 4 129 60 12 27.5 0.527 31 0 + 1 97 64 19 18.2 0.299 21 0 + 0 86 68 32 35.8 0.238 25 0 + 2 125 60 20 33.8 0.088 31 0 + 5 123 74 40 34.1 0.269 28 0 + 2 92 76 20 24.2 1.698 28 0 + 3 171 72 33 33.3 0.199 24 1 + 1 199 76 43 42.9 1.394 22 1 + 3 116 74 15 26.3 0.107 24 0 + 2 83 66 23 32.2 0.497 22 0 + 8 154 78 32 32.4 0.443 45 1 + 1 114 66 36 38.1 0.289 21 0 + 1 106 70 28 34.2 0.142 22 0 + 4 127 88 11 34.5 0.598 28 0 + 1 124 74 36 27.8 0.100 30 0 + 1 109 38 18 23.1 0.407 26 0 + 2 123 48 32 42.1 0.520 26 0 + 8 167 106 46 37.6 0.165 43 1 + 7 184 84 33 35.5 0.355 41 1 + 1 96 64 27 33.2 0.289 21 0 +10 129 76 28 35.9 0.280 39 0 + 6 92 62 32 32.0 0.085 46 0 + 6 109 60 27 25.0 0.206 27 0 + 5 139 80 35 31.6 0.361 25 1 + 6 134 70 23 35.4 0.542 29 1 + 3 106 54 21 30.9 0.292 24 0 + 0 131 66 40 34.3 0.196 22 1 + 0 135 94 46 40.6 0.284 26 0 + 5 158 84 41 39.4 0.395 29 1 + 3 112 74 30 31.6 0.197 25 1 + 8 181 68 36 30.1 0.615 60 1 + 2 121 70 32 39.1 0.886 23 0 + 1 168 88 29 35.0 0.905 52 1 + 1 144 82 46 46.1 0.335 46 1 + 2 101 58 17 24.2 0.614 23 0 + 2 96 68 13 21.1 0.647 26 0 + 3 107 62 13 22.9 0.678 23 1 +12 121 78 17 26.5 0.259 62 0 + 2 100 64 23 29.7 0.368 21 0 + 4 154 72 29 31.3 0.338 37 0 + 6 125 78 31 27.6 0.565 49 1 +10 125 70 26 31.1 0.205 41 1 + 2 122 76 27 35.9 0.483 26 0 + 2 114 68 22 28.7 0.092 25 0 + 1 115 70 30 34.6 0.529 32 1 + 7 114 76 17 23.8 0.466 31 0 + 2 115 64 22 30.8 0.421 21 0 + 1 130 60 23 28.6 0.692 21 0 + 1 79 75 30 32.0 0.396 22 0 + 4 112 78 40 39.4 0.236 38 0 + 7 150 78 29 35.2 0.692 54 1 + 1 91 54 25 25.2 0.234 23 0 + 1 100 72 12 25.3 0.658 28 0 +12 140 82 43 39.2 0.528 58 1 + 4 110 76 20 28.4 0.118 27 0 + 2 94 76 18 31.6 0.649 23 0 + 2 84 50 23 30.4 0.968 21 0 +10 148 84 48 37.6 1.001 51 1 + 3 61 82 28 34.4 0.243 46 0 + 4 117 62 12 29.7 0.380 30 1 + 3 99 80 11 19.3 0.284 30 0 + 3 80 82 31 34.2 1.292 27 1 + 4 154 62 31 32.8 0.237 23 0 + 6 103 72 32 37.7 0.324 55 0 + 6 111 64 39 34.2 0.260 24 0 + 0 124 70 20 27.4 0.254 36 1 + 1 143 74 22 26.2 0.256 21 0 + 1 81 74 41 46.3 1.096 32 0 + 4 189 110 31 28.5 0.680 37 0 + 4 116 72 12 22.1 0.463 37 0 + 7 103 66 32 39.1 0.344 31 1 + 8 124 76 24 28.7 0.687 52 1 + 1 71 78 50 33.2 0.422 21 0 + 0 137 84 27 27.3 0.231 59 0 + 9 112 82 32 34.2 0.260 36 1 + 4 148 60 27 30.9 0.150 29 1 + 1 136 74 50 37.4 0.399 24 0 + 9 145 80 46 37.9 0.637 40 1 + 1 93 56 11 22.5 0.417 22 0 + 1 107 72 30 30.8 0.821 24 0 +12 151 70 40 41.8 0.742 38 1 + 1 97 70 40 38.1 0.218 30 0 + 5 144 82 26 32.0 0.452 58 1 + 2 112 86 42 38.4 0.246 28 0 + 2 99 52 15 24.6 0.637 21 0 + 1 109 56 21 25.2 0.833 23 0 + 1 120 80 48 38.9 1.162 41 0 + 7 187 68 39 37.7 0.254 41 1 + 3 129 92 49 36.4 0.968 32 1 + 7 179 95 31 34.2 0.164 60 0 + 6 80 66 30 26.2 0.313 41 0 + 2 105 58 40 34.9 0.225 25 0 + 3 191 68 15 30.9 0.299 34 0 + 0 95 80 45 36.5 0.330 26 0 + 4 99 72 17 25.6 0.294 28 0 + 0 137 68 14 24.8 0.143 21 0 + 1 97 70 15 18.2 0.147 21 0 + 0 100 88 60 46.8 0.962 31 0 + 1 167 74 17 23.4 0.447 33 1 + 0 180 90 26 36.5 0.314 35 1 + 2 122 70 27 36.8 0.340 27 0 + 1 90 62 12 27.2 0.580 24 0 + 3 120 70 30 42.9 0.452 30 0 + 6 154 78 41 46.1 0.571 27 0 + 2 56 56 28 24.2 0.332 22 0 + 0 177 60 29 34.6 1.072 21 1 + 3 124 80 33 33.2 0.305 26 0 + 8 85 55 20 24.4 0.136 42 0 +12 88 74 40 35.3 0.378 48 0 + 9 152 78 34 34.2 0.893 33 1 + 0 198 66 32 41.3 0.502 28 1 + 0 188 82 14 32.0 0.682 22 1 + 5 139 64 35 28.6 0.411 26 0 + 7 168 88 42 38.2 0.787 40 1 + 2 197 70 99 34.7 0.575 62 1 + 2 142 82 18 24.7 0.761 21 0 + 8 126 74 38 25.9 0.162 39 0 + 3 158 76 36 31.6 0.851 28 1 + 3 130 78 23 28.4 0.323 34 1 + 2 100 54 28 37.8 0.498 24 0 + 1 164 82 43 32.8 0.341 50 0 + 4 95 60 32 35.4 0.284 28 0 + 2 122 52 43 36.2 0.816 28 0 + 4 85 58 22 27.8 0.306 28 0 + 0 151 90 46 42.1 0.371 21 1 + 6 144 72 27 33.9 0.255 40 0 + 3 111 90 12 28.4 0.495 29 0 + 1 107 68 19 26.5 0.165 24 0 + 6 115 60 39 33.7 0.245 40 1 + 5 105 72 29 36.9 0.159 28 0 + 7 194 68 28 35.9 0.745 41 1 + 4 184 78 39 37.0 0.264 31 1 + 0 95 85 25 37.4 0.247 24 1 + 7 124 70 33 25.5 0.161 37 0 + 1 111 62 13 24.0 0.138 23 0 + 7 137 90 41 32.0 0.391 39 0 + 9 57 80 37 32.8 0.096 41 0 + 2 157 74 35 39.4 0.134 30 0 + 2 95 54 14 26.1 0.748 22 0 +12 140 85 33 37.4 0.244 41 0 + 0 117 66 31 30.8 0.493 22 0 + 8 100 74 40 39.4 0.661 43 1 + 9 123 70 44 33.1 0.374 40 0 + 0 138 60 35 34.6 0.534 21 1 +14 100 78 25 36.6 0.412 46 1 +14 175 62 30 33.6 0.212 38 1 + 0 74 52 10 27.8 0.269 22 0 + 1 133 102 28 32.8 0.234 45 1 + 0 119 64 18 34.9 0.725 23 0 + 5 155 84 44 38.7 0.619 34 0 + 1 128 48 45 40.5 0.613 24 1 + 2 112 68 22 34.1 0.315 26 0 + 1 140 74 26 24.1 0.828 23 0 + 2 141 58 34 25.4 0.699 24 0 + 7 129 68 49 38.5 0.439 43 1 + 0 106 70 37 39.4 0.605 22 0 + 1 118 58 36 33.3 0.261 23 0 + 8 155 62 26 34.0 0.543 46 1 diff --git a/toolbox/mci/data/synth_te.txt b/toolbox/mci/data/synth_te.txt new file mode 100644 index 00000000..52440a05 --- /dev/null +++ b/toolbox/mci/data/synth_te.txt @@ -0,0 +1,1000 @@ + -0.970990139 0.429424950 0 + -0.631997027 0.251952852 0 + -0.773605760 0.690750778 0 + -0.606211523 0.175677956 0 + -0.539409005 0.376744239 0 + -0.960325850 0.110040710 0 + -1.041375608 0.328508085 0 + -0.822600536 0.175874200 0 + -0.943714771 -0.180633309 0 + -0.968763299 0.296070217 0 + -0.853637980 0.644010559 0 + -0.771994930 0.476344773 0 + -0.718952712 0.090457675 0 + -0.539520701 0.447837856 0 + -0.540093447 0.551067215 0 + -0.792923186 0.531235891 0 + -0.861472850 0.287352652 0 + -0.470131571 0.544251260 0 + -0.770683778 0.482733051 0 + -0.803031230 0.228632039 0 + -0.962520756 0.367759881 0 + -0.681960494 0.495354977 0 + -0.433007837 0.213645636 0 + -0.336831640 0.293614869 0 + -0.696425307 0.315194495 0 + -0.355766886 0.269794553 0 + -0.547898136 0.277054714 0 + -0.799663889 0.292931173 0 + -0.780012402 0.038437662 0 + -0.853938355 0.198423604 0 + -0.896295454 0.286916469 0 + -0.824028270 0.295231859 0 + -0.901075546 0.321018371 0 + -0.556718720 0.358145252 0 + -0.871004652 0.258992681 0 + -0.800820459 0.363123198 0 + -0.699003238 0.417050087 0 + -0.759409251 0.366156047 0 + -0.775268090 0.306716684 0 + -0.893576947 -0.096908084 0 + -0.284857192 0.307321395 0 + -0.665571750 0.365820514 0 + -0.741374392 0.298498149 0 + -0.767733049 0.245811163 0 + -0.779306345 0.319092986 0 + -0.892190952 0.201459901 0 + -0.122811626 0.516497113 0 + -0.731730651 0.055992550 0 + -1.011976425 0.344692082 0 + -0.573762197 0.059676643 0 + -0.641425285 0.333730563 0 + -0.985902178 0.162020997 0 + -0.661140507 0.136840396 0 + -0.749218489 0.185148533 0 + -0.540329548 0.387396621 0 + -0.592092859 0.447510299 0 + -0.860077357 0.218917745 0 + -0.867516891 -0.137491677 0 + -0.590055695 0.466004783 0 + -0.775966325 0.403399745 0 + -0.849687489 0.315466589 0 + -0.746283040 0.256242513 0 + -0.700854929 0.518361424 0 + -0.923680439 0.449453255 0 + -0.912092992 0.407980138 0 + -0.650765709 0.412200546 0 + -0.980330108 0.299281948 0 + -0.744408938 0.203087089 0 + -0.604170665 0.326156917 0 + -0.735903002 0.655288145 0 + -0.643607616 0.513819006 0 + -0.963376987 0.249000843 0 + -0.426980732 0.282178155 0 + -0.654762824 0.562181098 0 + -0.843491783 0.345421521 0 + -0.553968009 0.538960351 0 + -0.716946447 0.122102049 0 + -0.775328790 0.498892271 0 + -0.640289822 0.435762487 0 + -0.516878864 0.182337108 0 + -0.952125366 0.298280511 0 + -0.723017513 0.256182935 0 + -0.658805240 0.269147489 0 + -0.464552773 0.218324319 0 + -0.564517221 0.196511498 0 + -0.814096964 0.228304066 0 + -0.396184143 0.511765539 0 + -0.996637001 0.209223029 0 + -0.815950989 0.235966820 0 + -0.526626592 0.418687316 0 + -0.667763995 0.428833798 0 + -0.658898181 0.031828081 0 + -0.923935948 0.530254142 0 + -0.909973792 0.451785093 0 + -0.410551229 0.252159645 0 + -0.462064440 0.230673805 0 + -0.366146922 -0.036140226 0 + -0.595861370 0.400288539 0 + -0.704392096 0.238984335 0 + -0.841225771 0.577095745 0 + -0.969828933 0.155360193 0 + -0.557037265 0.314190393 0 + -0.671104208 0.361767035 0 + -0.503286446 0.566417412 0 + -0.950325858 0.078493347 0 + -0.675813120 0.319308250 0 + -0.831561973 0.143581661 0 + -0.435074090 0.492855894 0 + -0.793021028 0.118140919 0 + -0.848627588 0.082762982 0 + -0.820269797 0.395714263 0 + -0.422092727 0.477760711 0 + -0.408676218 0.374918252 0 + -0.546953839 0.473748255 0 + -0.735444130 0.266138774 0 + -0.582205470 0.271991191 0 + -0.338346632 0.242426860 0 + -0.535045557 0.118043648 0 + -0.493743519 0.717856305 0 + -0.760932705 0.416245530 0 + -0.515677444 0.184242721 0 + -0.673504588 0.296239478 0 + -0.459705697 0.186931282 0 + -0.694881314 0.381840980 0 + -0.387447545 0.080890693 0 + -0.596036129 0.184974829 0 + -0.664372536 0.423940859 0 + -0.883742635 0.614943083 0 + -0.509344933 0.290033636 0 + -0.925124882 0.604748154 0 + -0.841007867 0.290327096 0 + -0.894120137 0.157169952 0 + -0.646573229 0.609447746 0 + -1.017873059 0.148721295 0 + -0.582528753 0.184940557 0 + -0.897329196 0.532091737 0 + -0.465016860 0.285520226 0 + -0.726508681 0.181867205 0 + -0.514352969 0.156961029 0 + -0.739246011 0.408845252 0 + -0.537049319 0.307417180 0 + -0.923407832 0.492249753 0 + -0.663217181 0.241275721 0 + -0.871900824 0.191786697 0 + -0.574764695 0.216699985 0 + -0.778723382 0.417127421 0 + -0.717491428 0.169911784 0 + -0.293985190 0.341692708 0 + -0.732183039 0.611673182 0 + -0.672451661 0.290330390 0 + -0.392906014 0.314507904 0 + -0.821496561 0.383502471 0 + -0.441649840 0.131552989 0 + -0.734149425 0.138366727 0 + -0.353467324 0.403725989 0 + -0.756729286 0.140926608 0 + -0.985271855 0.307051129 0 + -0.734362749 0.131915653 0 + -0.843814454 0.508797861 0 + -0.871470989 0.409534472 0 + -0.643774042 0.386072579 0 + -0.617659001 0.067340392 0 + -0.282068649 0.693923139 0 + -0.402555368 0.204385656 0 + -0.458583969 0.420739380 0 + -0.846296983 0.277152491 0 + -1.048542317 0.338822747 0 + -0.799795307 0.309430762 0 + -0.852040552 0.307281614 0 + -0.616474678 0.252952510 0 + -0.691690351 0.272750414 0 + -0.809142202 0.441901584 0 + -0.837139722 0.269171931 0 + -0.743520251 0.247417602 0 + -0.660650230 -0.028489077 0 + -0.594815839 0.109164679 0 + -0.597128033 -0.037465241 0 + -0.921420258 -0.069844290 0 + -0.877566913 0.304297059 0 + -0.765371773 0.596974416 0 + -0.699840550 0.167126769 0 + -0.523434825 -0.064742897 0 + -0.656387744 0.012460495 0 + -1.036967640 0.141450813 0 + -0.715165192 0.217239838 0 + -0.747858131 0.569994813 0 + -0.625684541 0.320122450 0 + -0.756699924 0.174518616 0 + -0.679690670 0.438410861 0 + -0.612004202 -0.134269826 0 + -0.647906789 0.239638558 0 + -0.691066413 0.255635309 0 + -0.675112764 0.550169559 0 + -0.851072790 0.474955936 0 + -0.837051482 0.408050507 0 + -0.961405831 0.588207922 0 + -0.642774716 0.163487304 0 + -0.892075711 0.064132978 0 + -0.927798777 0.072240031 0 + -0.751800726 0.409258566 0 + -0.805341030 0.064157327 0 + -0.692838235 0.171715163 0 + -0.703943931 0.476730183 0 + -0.694804098 0.268655402 0 + -0.567758798 0.207116645 0 + -0.822380000 0.268404036 0 + -0.565082539 0.327015498 0 + -0.724181702 0.625763803 0 + -0.916357511 0.236124996 0 + -0.430182548 0.268033748 0 + -0.632645741 0.522382761 0 + -0.850972862 0.345168936 0 + -0.609691020 0.501872186 0 + -0.705661024 0.220694983 0 + -0.693161871 0.100244402 0 + -0.633922642 0.390701059 0 + -0.710406768 0.015180240 0 + -1.055052036 0.517833140 0 + -0.621276063 0.167382599 0 + -0.613423246 0.266134950 0 + -0.989565379 0.166693580 0 + -0.923580375 0.412606504 0 + -0.889581095 0.426760653 0 + -0.930040388 0.240533824 0 + -0.691421356 0.006339557 0 + -1.031412255 0.482277646 0 + -0.701394895 0.462356010 0 + -0.627721178 0.243813111 0 + -0.829380326 0.487867261 0 + -0.612200851 0.121715064 0 + -0.528139634 0.449962538 0 + -0.616674472 0.058254182 0 + -0.649202842 0.263909873 0 + -0.655384302 0.225793561 0 + -0.750085240 0.119545244 0 + -0.471920626 0.278830975 0 + -0.219905912 0.315052974 0 + -0.871701260 0.240570129 0 + -0.730197977 0.295504781 0 + -0.620676222 0.046383576 0 + -0.657830687 0.265899761 0 + -0.475352116 0.279850946 0 + -0.734794644 0.365235616 0 + -0.772673638 0.355477724 0 + -0.620710470 0.770796635 0 + -0.529626406 0.091067609 0 + -0.730846476 0.642803364 0 + -0.938694493 0.324275071 0 + -0.723706354 -0.017999841 0 + -0.979569099 -0.003034376 0 + 0.448754392 0.015050386 0 + -0.077907282 0.245842052 0 + 0.316786631 0.252917817 0 + 0.229597046 0.067681573 0 + 0.197949376 0.310003887 0 + 0.048404642 -0.037865268 0 + 0.270601003 0.260199166 0 + 0.516192043 0.258256258 0 + 0.154718993 0.040306842 0 + -0.005611276 0.223658499 0 + 0.365076313 -0.001956641 0 + 0.086615547 0.138482814 0 + 0.198645891 0.047611642 0 + 0.131870660 0.402255360 0 + 0.585894768 0.433203159 0 + -0.023498655 0.379919943 0 + 0.394174061 0.533936878 0 + 0.595983773 0.680516952 0 + 0.388419733 0.321931614 0 + 0.270452263 0.360309566 0 + 0.336909893 0.176262915 0 + 0.481432232 0.326027716 0 + 0.246865240 0.532700400 0 + -0.020439631 0.132155124 0 + 0.389941424 0.309223343 0 + 0.048115168 0.104763308 0 + 0.284816331 -0.048775617 0 + 0.529166911 0.285314795 0 + 0.349208427 0.063167392 0 + 0.323888259 0.192358455 0 + 0.321213977 0.101190083 0 + 0.303365953 0.286689359 0 + -0.075979803 0.312196126 0 + 0.317894059 0.110578558 0 + 0.136145272 0.223509762 0 + 0.086777443 0.397316175 0 + 0.330555298 -0.018831347 0 + 0.202260475 0.212061643 0 + 0.276704436 0.541792424 0 + 0.244814590 -0.033434890 0 + 0.429043775 0.183967494 0 + 0.340412789 0.237474210 0 + 0.382064022 0.123295299 0 + 0.381833239 0.085809636 0 + 0.424417864 0.321954582 0 + 0.206306313 0.348957865 0 + 0.091614953 0.309132098 0 + 0.627597689 0.472188745 0 + 0.270244718 0.361936451 0 + 0.127928396 0.368238186 0 + 0.399192895 0.120050819 0 + 0.450618123 0.452328633 0 + 0.254900382 0.410220018 0 + 0.259523390 0.124427489 0 + 0.417004689 0.300805900 0 + 0.346581338 0.283479475 0 + 0.748854615 0.246812787 0 + 0.428530072 0.636260298 0 + 0.127369504 0.321732050 0 + 0.528722462 0.227075837 0 + 0.618168220 0.327309276 0 + 0.286029472 0.215643450 0 + 0.142578461 0.112955825 0 + 0.282764909 0.091628143 0 + 0.788220007 0.464545152 0 + 0.119165220 0.239567886 0 + 0.244772936 0.014906673 0 + 0.160442893 0.455259044 0 + 0.454067300 0.332582882 0 + -0.057868287 0.498675578 0 + -0.111365306 0.079756044 0 + 0.198824819 0.476017542 0 + 0.595468169 0.162120124 0 + 0.085627364 0.315262031 0 + 0.465261497 0.123331422 0 + 0.359673625 0.364504393 0 + 0.111822093 0.296370162 0 + 0.509269078 0.464037322 0 + 0.470888018 0.285556829 0 + 0.393262912 0.093782124 0 + 0.311897634 0.286626364 0 + 0.151594554 0.268411495 0 + 0.084423498 0.319282396 0 + 0.208641564 0.230226362 0 + 0.361230606 0.506867239 0 + 0.425667999 0.239049251 0 + 0.399549324 0.136827304 0 + 0.279615939 0.310402719 0 + 0.109049911 0.630255432 0 + 0.102929855 0.446152743 0 + 0.551085316 0.313983603 0 + 0.579201159 0.179353765 0 + 0.356514867 0.178396614 0 + 0.259861364 0.096917764 0 + 0.545480531 0.272730569 0 + 0.398789597 0.149343536 0 + 0.383441254 0.243298247 0 + 0.405415302 0.351024129 0 + 0.249091946 0.423059272 0 + 0.293535767 0.133960638 0 + 0.149869213 0.305675082 0 + 0.224986842 0.464864831 0 + 0.240826479 0.233973445 0 + 0.122917552 0.406179372 0 + 0.301231733 0.178773911 0 + 0.257698819 0.537312141 0 + 0.446288764 0.206483371 0 + 0.511214849 0.156330717 0 + 0.474675267 0.454212426 0 + 0.373402327 0.107531816 0 + 0.453575217 0.013564367 0 + 0.363708989 0.324209899 0 + 0.323172397 0.308234424 0 + 0.263568182 0.097321560 0 + 0.375989273 0.511128488 0 + 0.483416817 -0.027606822 0 + 0.412708967 0.353260156 0 + 0.294590710 0.338631607 0 + 0.148425126 0.313998286 0 + 0.476236614 0.009138517 0 + 0.051021769 0.518229423 0 + 0.488029582 0.492206314 0 + 0.193703118 0.356127440 0 + 0.390385684 0.402548715 0 + 0.166515062 0.077486533 0 + 0.378346001 0.205554127 0 + 0.059890677 0.615481812 0 + -0.077252668 0.325973024 0 + 0.519325984 0.352901733 0 + 0.271955420 0.031010063 0 + 0.027254987 0.289394991 0 + 0.437437673 -0.027210937 0 + 0.028370640 0.166304765 0 + 0.433657082 0.604909277 0 + 0.280505393 0.022916023 0 + 0.300735977 0.188023897 0 + 0.182031568 0.292354741 0 + 0.316158641 0.423973591 0 + 0.530601146 0.287109075 0 + 0.210237556 0.384357431 0 + 0.399444521 0.496882692 0 + 0.272113433 0.437262474 0 + 0.418146305 0.145521656 0 + 0.504825239 0.154106314 0 + 0.166974207 0.180641380 0 + 0.106527356 0.500370591 0 + 0.607348514 0.184680121 0 + 0.517847638 0.396858357 0 + 0.231553652 0.403086636 0 + 0.255029497 0.430592319 0 + 0.287511011 0.219412906 0 + 0.200852107 0.272097495 0 + 0.226547849 0.244596483 0 + 0.011878373 0.352803074 0 + 0.380569910 0.434089493 0 + 0.519215428 0.072764703 0 + 0.623854880 0.338983888 0 + 0.183173455 0.255322403 0 + 0.226420389 0.075341621 0 + 0.455356509 0.367957232 0 + 0.332301375 -0.011058516 0 + 0.376306021 0.188460770 0 + 0.428169526 0.054583036 0 + 0.145829529 0.368253163 0 + 0.493757540 0.376063674 0 + 0.529391969 0.074698658 0 + 0.409826160 0.280322788 0 + 0.612354746 0.120926664 0 + 0.221568084 0.273458368 0 + 0.427545649 0.106200846 0 + 0.533325611 0.591671136 0 + 0.462109537 0.357955560 0 + 0.182362120 0.298520960 0 + 0.310107790 0.301510248 0 + 0.159799550 0.257640193 0 + 0.254288145 0.374308080 0 + 0.316374077 0.029411804 0 + 0.285942260 0.338773678 0 + 0.552541865 -0.016858031 0 + -0.004090460 0.399012387 0 + 0.060484031 0.277592649 0 + 0.545097739 0.218461339 0 + 0.268284924 0.267903340 0 + 0.159022649 0.531382417 0 + 0.492658208 0.486286052 0 + -0.128240252 0.533333926 0 + 0.447760080 0.284865402 0 + 0.239374886 0.462386877 0 + 0.138634894 0.395550274 0 + 0.417284343 0.200022118 0 + 0.178303979 0.306720386 0 + 0.221552636 0.396534895 0 + -0.009120409 0.724738825 0 + 0.292748806 0.414432640 0 + 0.300563713 0.214325496 0 + 0.242506812 0.232690286 0 + 0.234494302 0.247006083 0 + 0.352550448 0.351581175 0 + 0.185994378 0.269914887 0 + 0.409680307 0.212370722 0 + 0.163919950 0.026130185 0 + 0.169756191 0.104358886 0 + 0.354398935 0.227524046 0 + 0.388870060 0.042378087 0 + 0.344788486 0.246053805 0 + 0.193145216 0.271352787 0 + 0.430800164 0.263193765 0 + 0.232808591 0.445516712 0 + 0.326059317 0.563886858 0 + 0.330837091 0.256040145 0 + 0.323691216 0.356872920 0 + 0.367737090 -0.088857683 0 + 0.530750561 0.327389964 0 + 0.089596372 0.338423910 0 + 0.432192982 0.394261493 0 + 0.186694048 0.438187113 0 + 0.458275145 0.324647633 0 + 0.480078071 0.374810492 0 + 0.582758378 0.390433695 0 + 0.437808065 0.389265557 0 + 0.208830936 0.010096493 0 + 0.377797466 0.474572076 0 + 0.183803076 -0.090083970 0 + 0.155682547 0.537563127 0 + 0.071926861 0.572783083 0 + 0.364435618 -0.123841713 0 + 0.408213991 0.254483065 0 + 0.466073956 0.398618252 0 + 0.614281743 0.283302172 0 + -0.047151673 0.214579449 0 + 0.326917150 0.468066389 0 + 0.458840582 0.443470083 0 + 0.109537926 0.189505910 0 + 0.161895892 0.123705078 0 + 0.450055408 0.501518844 0 + 0.368869484 0.557190529 0 + 0.334209119 0.413960488 0 + -0.031121068 0.228014456 0 + 0.176753850 0.430199990 0 + 0.552527788 0.224902508 0 + 0.304266409 0.220287210 0 + 0.210462653 0.415336683 0 + 0.063953710 0.045543235 0 + -0.063149684 0.351389125 0 + 0.073535710 0.252143534 0 + 0.665453703 0.203720086 0 + 0.539642761 0.279986737 0 + 0.250981585 0.069569958 0 + 0.392679888 0.090261998 0 + 0.431409216 0.288456378 0 + -0.516451834 0.501256111 1 + -0.116775286 0.483404773 1 + -0.327960793 0.546240228 1 + -0.394572192 0.755243715 1 + -0.110201988 0.553402230 1 + -0.160538577 0.579525838 1 + -0.124742465 0.323661757 1 + -0.109742769 0.696514698 1 + -0.687328305 0.807033124 1 + -0.358374262 0.807265743 1 + -0.335836520 0.392482381 1 + -0.321604223 0.591913273 1 + -0.091546228 0.562483354 1 + -0.660890881 0.611049023 1 + -0.561938441 0.907495412 1 + -0.244433911 0.451367292 1 + -0.392885460 0.550604753 1 + -0.429608736 0.644152661 1 + -0.090462865 0.522251590 1 + -0.436484641 0.520039359 1 + -0.519966218 0.940830736 1 + -0.418391404 1.011277424 1 + -0.405807798 0.738999068 1 + -0.085688384 0.847932361 1 + -0.210347223 0.416696729 1 + -0.531896660 0.452618557 1 + -0.294588066 0.846012850 1 + -0.092753982 0.693082777 1 + -0.314549926 0.797236706 1 + -0.262918395 0.787474678 1 + -0.389819133 0.579880509 1 + -0.162163174 0.315021403 1 + -0.418250429 0.684349895 1 + -0.356533257 0.896022491 1 + -0.461800168 0.782142975 1 + -0.149067005 0.837864969 1 + -0.376621128 0.553207248 1 + -0.235807559 0.642937572 1 + -0.433816383 0.568682995 1 + 0.003602461 0.804352974 1 + -0.286855152 0.710632583 1 + -0.424066790 0.994872459 1 + -0.270030002 0.833427152 1 + -0.239212386 0.378268423 1 + -0.255304685 0.822105360 1 + -0.196569409 0.703182679 1 + -0.125203354 0.844725933 1 + -0.338351441 0.680964321 1 + -0.383184405 0.839383812 1 + -0.398513962 0.750284450 1 + 0.027844709 0.537770177 1 + -0.295483256 0.846722230 1 + -0.552989277 0.794817114 1 + -0.004901838 0.608282407 1 + -0.029384352 0.614072912 1 + -0.444694587 0.779042878 1 + -0.338928122 0.789725990 1 + 0.122195503 0.784475027 1 + -0.186584991 0.560614872 1 + -0.295015658 0.840559001 1 + -0.102630670 0.675938267 1 + -0.430785693 0.645617846 1 + -0.099297566 0.894434898 1 + -0.009264193 1.012595196 1 + -0.560973647 0.807423104 1 + -0.536294204 0.529432752 1 + -0.563297476 0.646381268 1 + -0.292902091 0.620924549 1 + -0.107464304 0.615869773 1 + -0.261216307 0.699646352 1 + -0.105100716 0.868085863 1 + -0.362473095 0.683245848 1 + -0.548222187 0.726739882 1 + -0.522717054 0.636324411 1 + -0.406753361 0.858975870 1 + -0.272149948 1.009788333 1 + -0.058505372 0.722037722 1 + -0.286284031 0.564831018 1 + -0.145641743 0.527786275 1 + -0.254951568 0.909735133 1 + -0.200910922 0.911648155 1 + -0.397769966 0.398117280 1 + -0.547436085 0.779495789 1 + -0.231129177 0.491139768 1 + -0.473894736 0.682466158 1 + -0.231075189 0.453157246 1 + -0.268776826 0.676814477 1 + -0.180889587 0.880462410 1 + -0.326237906 0.599734095 1 + -0.252657163 0.575832499 1 + -0.294967226 0.707617098 1 + -0.441714737 0.649258390 1 + -0.434336942 0.859634714 1 + -0.080950672 0.608362742 1 + -0.256056671 0.465280126 1 + -0.767972482 0.818894418 1 + -0.250929687 0.807765177 1 + -0.233531508 0.536107452 1 + -0.166252171 0.578022234 1 + -0.399389870 0.961981117 1 + -0.383257048 0.918196737 1 + -0.246208261 0.728269018 1 + -0.112873567 0.825689335 1 + -0.096666032 0.707306804 1 + -0.457949369 0.704015342 1 + -0.255003562 0.504258034 1 + -0.073434667 0.722783609 1 + -0.409375468 0.526062925 1 + -0.363348126 0.881713044 1 + -0.257217769 0.607597755 1 + -0.349331300 0.703112332 1 + -0.151880213 0.492886000 1 + -0.404171363 0.737139545 1 + -0.462320910 0.423673110 1 + -0.546143281 0.835222198 1 + -0.229962943 0.611218821 1 + -0.246561278 0.550748181 1 + -0.392635644 0.396901704 1 + -0.175983074 0.659236133 1 + -0.160444346 0.856989440 1 + -0.341235994 0.536421185 1 + -0.333233675 0.558945553 1 + -0.274226030 0.677337101 1 + -0.394217634 1.084965709 1 + -0.177110920 1.174990894 1 + -0.403972304 0.705580257 1 + -0.387046408 0.654499407 1 + -0.044038573 0.753839485 1 + -0.278389636 0.349432166 1 + -0.272249470 0.234622985 1 + -0.191592271 0.380898603 1 + -0.590368203 0.698331693 1 + -0.374188840 0.819242381 1 + -0.351703587 0.730361507 1 + -0.281959049 0.469288157 1 + -0.751945036 0.885219702 1 + -0.306929899 0.574182522 1 + -0.762727447 0.890352701 1 + -0.564448380 0.729602705 1 + 0.040323664 0.779572618 1 + -0.462188702 0.998868915 1 + -0.447915766 0.843500207 1 + -0.217001799 0.796623800 1 + -0.112509220 0.611900551 1 + -0.131149777 0.948975611 1 + -0.403054671 0.786868546 1 + 0.008848708 0.652933806 1 + 0.090647590 0.654317764 1 + -0.358620932 0.936462477 1 + -0.441265488 0.326283245 1 + -0.479842420 0.788087594 1 + -0.588843824 0.648214630 1 + -0.562606783 0.754763105 1 + -0.514270007 0.324312047 1 + -0.392905106 0.821041597 1 + -0.075132059 0.685702990 1 + -0.196830870 0.714112820 1 + -0.301481674 0.552313534 1 + -0.181585205 0.659988770 1 + -0.114373131 0.736877415 1 + -0.331936585 0.440209520 1 + -0.266807581 0.545085006 1 + -0.475109818 0.947483833 1 + -0.557037972 0.778719573 1 + -0.193240214 0.574512048 1 + -0.029348731 0.829601881 1 + -0.383376526 0.624385592 1 + -0.035071125 0.812800625 1 + -0.060506093 0.772166835 1 + -0.160710931 0.530042141 1 + -0.210362275 0.567446850 1 + -0.283272444 0.798839816 1 + -0.520613526 0.837372559 1 + -0.263870495 0.687937002 1 + -0.060226406 0.688228649 1 + -0.429473669 0.654717940 1 + -0.325250467 0.791105596 1 + 0.094837102 0.750572909 1 + -0.326848641 0.823553280 1 + -0.537630937 0.827068887 1 + -0.589458171 0.897096209 1 + -0.255109811 0.737443245 1 + -0.350722503 0.739648314 1 + -0.111745167 0.705987527 1 + -0.213435551 0.466547665 1 + -0.272518877 0.683481004 1 + -0.440414101 0.974317798 1 + -0.303362790 0.576264653 1 + -0.221200040 0.987888085 1 + -0.286914561 0.619578181 1 + 0.096845361 0.511673423 1 + -0.363110834 0.661562448 1 + -0.211246704 0.813171823 1 + -0.222052903 0.686080299 1 + -0.321828330 0.624357510 1 + -0.473737950 0.506318972 1 + -0.212793549 0.774693470 1 + 0.008463870 0.614591369 1 + -0.205693420 0.644919563 1 + -0.378486601 0.778361218 1 + -0.229442899 0.594732866 1 + -0.162703081 0.930991126 1 + -0.321296905 0.828610911 1 + -0.400332594 0.688297191 1 + -0.312050685 0.618494750 1 + -0.039349153 0.959790721 1 + -0.273914659 0.599403497 1 + -0.348565665 0.612606769 1 + -0.413758325 0.696448995 1 + -0.098831839 0.854519409 1 + -0.287690535 0.883301183 1 + -0.383124103 0.672367628 1 + -0.561271474 1.067278573 1 + -0.166431846 0.897151624 1 + -0.635114720 0.688087392 1 + -0.332175204 0.501477407 1 + -0.474805835 0.711218005 1 + -0.116004389 0.708363990 1 + -0.477937453 0.702949001 1 + -0.126810442 0.971409951 1 + -0.156822576 0.457687275 1 + -0.293523863 0.856486819 1 + -0.129615545 0.891819146 1 + -0.108242313 0.644814421 1 + -0.501979824 0.370050434 1 + -0.138108021 0.612928438 1 + -0.179322731 0.366517387 1 + -0.458093963 0.571370985 1 + -0.028565637 0.486501211 1 + -0.426175577 0.461765467 1 + -0.310680953 0.544905689 1 + -0.180247439 0.876336671 1 + -0.217870537 0.390856979 1 + -0.315992257 0.736172703 1 + 0.236276902 0.714179743 1 + -0.185456072 0.702294953 1 + -0.203065705 0.317910002 1 + -0.296142711 0.648026589 1 + -0.448939545 0.650603998 1 + 0.077064746 0.797884087 1 + 0.034024500 0.788213418 1 + -0.439519067 0.946446539 1 + -0.471452461 0.708540945 1 + -0.263821096 0.565778110 1 + -0.676333519 1.064998541 1 + -0.394630195 0.732544473 1 + -0.334698783 0.638313660 1 + 0.043828297 0.782970773 1 + 0.073254562 0.639405607 1 + -0.358305948 0.638878595 1 + 0.289824646 0.645297701 1 + 0.479141353 0.769272264 1 + 0.180670084 0.518893193 1 + 0.199825830 0.747216818 1 + 0.735249202 0.833027044 1 + 0.249991814 0.350660256 1 + 0.413137889 0.854044549 1 + 0.518581462 0.386362750 1 + 0.465359263 0.854392557 1 + 0.348309276 0.680024754 1 + 0.174782318 0.544423218 1 + 0.549911988 0.472172493 1 + 0.203934276 0.410263392 1 + 0.338644108 1.028370469 1 + 0.161322119 0.950855699 1 + 0.350961307 0.686427652 1 + 0.090257414 0.846995122 1 + 0.764373743 0.615571296 1 + 0.414756998 0.893306725 1 + 0.679361421 0.659759084 1 + 0.640285978 0.804268545 1 + 0.630876040 0.710028594 1 + 0.366370214 0.772543364 1 + 0.314611449 0.755070836 1 + 0.745924055 0.706345767 1 + 0.489768059 0.684198041 1 + 0.075247977 0.621422345 1 + 0.499573139 0.679632119 1 + 0.350405143 0.443980792 1 + 0.636928363 0.603842916 1 + 0.224908918 0.840917922 1 + -0.032261912 0.655726651 1 + 0.627052189 0.808688697 1 + 0.263348975 0.455434849 1 + 0.520257017 0.762965338 1 + 0.151882522 0.966544141 1 + 0.098482589 0.517323437 1 + 0.201212077 0.549826846 1 + 0.371298202 0.761389940 1 + 0.497766489 0.769076360 1 + 0.409493154 0.305118700 1 + 0.340849813 0.766677739 1 + 0.391675543 0.489773920 1 + 0.516131854 0.412661585 1 + 0.522760611 0.520845425 1 + 0.446358722 0.869775036 1 + 0.224400728 0.559199836 1 + 0.583149627 0.871728559 1 + 0.420184227 0.768544337 1 + 0.340883764 0.582414682 1 + 0.407626346 1.016274588 1 + 0.226804848 0.997357208 1 + 0.461550030 0.728402685 1 + 0.275762111 0.773039119 1 + 0.304760108 0.405069957 1 + 0.636786149 0.521153930 1 + 0.544820787 0.902598154 1 + 0.816098957 0.643244361 1 + 0.454637082 0.627059827 1 + 0.416886517 0.498139441 1 + 0.585814059 0.472857968 1 + 0.158972903 0.877325952 1 + 0.218197123 0.791103192 1 + 0.436713777 0.582375556 1 + 0.465359340 0.619108530 1 + 0.346901746 0.776639489 1 + 0.599207277 0.605698565 1 + 0.463002935 0.972725613 1 + 0.694263789 0.550710864 1 + 1.000277812 0.669240364 1 + 0.503660224 0.451743317 1 + 0.609419010 0.560098000 1 + 0.352923549 0.639530833 1 + 0.313797682 0.428469344 1 + 0.275593847 0.624510853 1 + 0.310310776 0.757815199 1 + 0.200769573 1.068014129 1 + 0.393611386 0.489922085 1 + 0.293284180 0.564537846 1 + 0.150904334 0.874953285 1 + 0.359648477 0.984800311 1 + 0.425437016 0.605205704 1 + 0.550057275 0.953322346 1 + 0.369377777 0.717383758 1 + 0.483823544 0.776401643 1 + 0.665201554 0.609337149 1 + 0.367662676 0.432857589 1 + 0.603654120 0.439204275 1 + 0.361992913 0.607744455 1 + 0.365320313 0.193465958 1 + 0.565587013 0.766374185 1 + 0.459978544 0.421990201 1 + 0.389662454 0.697573566 1 + 0.662029374 0.545080251 1 + 0.193287037 0.660104813 1 + 0.770581129 0.678276952 1 + 0.517729293 0.709447233 1 + 0.666759179 0.738395921 1 + 0.507357601 0.504291821 1 + 0.074897782 0.726624656 1 + 0.267419803 0.669125800 1 + 0.570998498 0.905961669 1 + 0.234076185 0.680851488 1 + 0.204728441 0.915150466 1 + 0.463600872 0.831022543 1 + 0.551695270 0.877530083 1 + 0.375064997 0.706265086 1 + 0.548113044 0.683542273 1 + 0.436411367 0.523946916 1 + 0.171669265 0.706402907 1 + 0.228628170 0.696358973 1 + 0.258176000 0.750019031 1 + 0.427636052 0.726640752 1 + 0.551129128 1.041844415 1 + 0.382357212 0.485587245 1 + 0.627187520 0.857796470 1 + 0.759430378 0.897903714 1 + 0.385966401 0.649098802 1 + 0.216206061 0.886147391 1 + 0.107421934 0.525437056 1 + 0.466619974 0.649300564 1 + 0.483552867 0.519368234 1 + 0.188288155 0.704849311 1 + 0.123111648 0.618943465 1 + 0.149201404 0.674098357 1 + 0.541125439 0.641048950 1 + 0.707584972 1.048980926 1 + 0.250259605 0.738434506 1 + 0.388929309 0.980538827 1 + 0.163559795 0.768820434 1 + 0.290938989 0.858416660 1 + 0.671326658 0.887569891 1 + 0.419646183 0.833301601 1 + 0.297576300 0.815635781 1 + 0.488205349 0.928912516 1 + 0.274956333 0.622947292 1 + 0.364636103 0.552039161 1 + 0.020765563 0.400801476 1 + 0.503582267 0.462402974 1 + 0.129743512 0.478205376 1 + 0.205737679 0.652800375 1 + 0.491663362 0.919029482 1 + 0.541928820 0.592238748 1 + 0.352448258 0.438954474 1 + 0.340546986 0.610581184 1 + 0.087362845 0.722352081 1 + 0.544510425 0.310570940 1 + 0.426834451 0.697519317 1 + 0.505026501 0.203961507 1 + 0.393952243 0.701709243 1 + 0.341212359 0.487823226 1 + 0.443882109 0.515215865 1 + 0.216623801 0.641423278 1 + 0.325421774 0.565006133 1 + 0.339954219 0.500219969 1 + 0.757953402 0.646113630 1 + 0.166511560 0.675639720 1 + 0.394924171 0.795156547 1 + 0.581373272 0.769434777 1 + 0.469451043 0.686613394 1 + 0.180074959 0.917903510 1 + 0.314960733 0.919406796 1 + 0.781475499 1.074871466 1 + 0.261043992 0.883671133 1 + 0.149151175 0.475484999 1 + 0.236371870 0.975832107 1 + 0.646323770 0.522312176 1 + 0.518347874 0.876936157 1 + 0.089471338 0.658664051 1 + 0.498070451 0.902620720 1 + 0.248059552 0.746906831 1 + 0.550195316 0.737298487 1 + 0.280602842 0.603132684 1 + 0.431834416 0.533887741 1 + 0.267799611 0.603699345 1 + 0.507750995 0.826989974 1 + -0.064478127 0.834070122 1 + 0.342112413 0.661643764 1 + 0.332313982 0.509083774 1 + 0.665012582 0.878512787 1 + 0.382910589 0.749228951 1 + 0.361027556 0.645111929 1 + 0.571981147 0.794214002 1 + 0.536918322 0.898472992 1 + 0.331872670 0.570367930 1 + 0.044037168 0.476641964 1 + 0.410716663 0.798924771 1 + 0.455083777 0.551831167 1 + 0.474594596 0.889946347 1 + 0.413672127 0.867650039 1 + 0.682171442 0.972182362 1 + 0.425353451 0.535316350 1 + 0.262277420 0.637457666 1 + 0.007860344 0.806598462 1 + 0.380999590 0.653580787 1 + 0.538437280 0.907997360 1 + 0.180415465 0.914334885 1 + 0.237060285 0.752505492 1 + 0.829663295 0.697894513 1 + 0.307664951 1.074702414 1 + 0.239849381 0.753987444 1 + 0.275375404 0.806554305 1 + 0.416984789 0.452953422 1 + 0.476493007 0.858473259 1 + 0.564497576 0.915314697 1 + 0.198295169 0.534934547 1 + 0.294198911 0.374100529 1 + 0.684760671 0.892746414 1 + 0.168075136 0.794230658 1 + 0.502763522 0.712129784 1 + 0.129722603 0.697110450 1 + 0.285983065 0.796121883 1 + 0.097239329 0.681159777 1 + 0.210574775 0.792652629 1 + 0.593896992 0.530407106 1 + 0.358836790 0.671400853 1 + 0.197591638 0.710584968 1 + 0.540587182 0.774780451 1 + 0.175106338 0.609394118 1 + 0.448304389 0.663333083 1 + 0.289880687 0.204721503 1 + 0.300130047 0.934825869 1 + 0.152511070 0.851596486 1 + 0.495317475 0.631046756 1 + 0.072423805 0.678667079 1 + 0.500846416 0.689706961 1 + 0.159104712 0.628206422 1 + 0.710308164 0.777809751 1 + 0.750642087 0.828037270 1 + 0.559868855 0.783081248 1 + 0.400801648 0.786167018 1 + 0.356480531 0.911823818 1 + 0.844132265 0.561509712 1 + 0.426337951 0.777438407 1 + 0.461052514 0.615763585 1 + 0.205997206 0.785369909 1 + 0.118613656 0.832647177 1 + 0.444428480 0.747145725 1 + 0.278467451 0.755943870 1 + 0.329683958 0.704522943 1 + 0.338924385 0.739418880 1 + 0.427674817 0.962589298 1 + 0.324169980 0.808410845 1 + 0.526486063 0.856427139 1 + 0.664857776 0.773954077 1 + 0.327675416 0.608013752 1 + 0.247589562 0.279270348 1 + 0.418514564 1.044157214 1 + 0.232314519 0.819642835 1 + 0.762040971 0.573218465 1 diff --git a/toolbox/mci/data/synth_tr.txt b/toolbox/mci/data/synth_tr.txt new file mode 100644 index 00000000..9a64a283 --- /dev/null +++ b/toolbox/mci/data/synth_tr.txt @@ -0,0 +1,250 @@ + 0.05100797 0.16086164 0 + -0.74807425 0.08904024 0 + -0.77293371 0.26317168 0 + 0.21837360 0.12706142 0 + 0.37268336 0.49656200 0 + -0.62931544 0.63202159 0 + -0.43307167 0.14479166 0 + -0.84151970 -0.19131316 0 + 0.47525648 0.22483671 0 + 0.32082976 0.32721288 0 + 0.32061253 0.33407547 0 + -0.89077472 0.41168783 0 + 0.17850119 0.44691359 0 + 0.31558002 0.38853383 0 + 0.55777224 0.47272748 0 + 0.03191877 0.01222964 0 + 0.25090585 0.30716705 0 + 0.23571547 0.22493837 0 + -0.07236203 0.33376524 0 + 0.50440241 0.08054579 0 + -0.63223351 0.44552458 0 + -0.76784656 0.23614689 0 + -0.70017557 0.21038848 0 + -0.64713491 0.15921366 0 + -0.76739248 0.09259038 0 + -0.51788734 0.03288107 0 + 0.17516644 0.34534871 0 + -0.68031190 0.47612156 0 + 0.01595199 0.32167526 0 + -0.71481078 0.51421443 0 + 0.07837946 0.32284981 0 + -0.80872251 0.47036593 0 + -0.84211234 0.09294232 0 + -0.98591577 0.48309267 0 + 0.29104081 0.34275967 0 + 0.24321541 0.51488295 0 + -0.60104419 0.05060116 0 + -1.24652451 0.45923165 0 + -0.82769016 0.36187460 0 + -0.62117301 -0.10912158 0 + -0.70584105 0.65907662 0 + 0.06718867 0.60574850 0 + 0.30505147 0.47417973 0 + 0.60788138 0.39361588 0 + -0.78937483 0.17591675 0 + -0.53123209 0.42652809 0 + 0.25202071 0.17029707 0 + -0.57880357 0.26553665 0 + -0.83176749 0.54447377 0 + -0.69859164 0.38566851 0 + -0.73642607 0.11857527 0 + -0.93496195 0.11370707 0 + 0.43959309 0.41430638 0 + -0.54690854 0.24956276 0 + -0.08405550 0.36521058 0 + 0.32211458 0.69087105 0 + 0.10764739 0.57946932 0 + -0.71864030 0.25645757 0 + -0.87877752 0.45064757 0 + -0.69846046 0.95053870 0 + 0.39757434 0.11810207 0 + -0.50451354 0.57196376 0 + 0.25023622 0.39783889 0 + 0.61709156 0.10185808 0 + 0.31832860 0.08790562 0 + -0.57453363 0.18624195 0 + 0.09761865 0.55176786 0 + 0.48449339 0.35372973 0 + 0.52400684 0.46616851 0 + -0.78138463 -0.07534713 0 + -0.49704591 0.59948077 0 + -0.96984525 0.46624927 0 + 0.43541407 0.12192386 0 + -0.67942462 0.30753942 0 + -0.62529036 0.07099046 0 + -0.02318116 0.40442601 0 + 0.23200141 0.71066846 0 + 0.09384354 0.46674396 0 + 0.14234301 0.17898711 0 + -0.61686357 0.25507763 0 + 0.23636288 0.51543839 0 + 0.38914177 0.40429568 0 + -0.95178678 -0.03772239 0 + 0.24087822 0.71948890 0 + 0.12446266 0.45178849 0 + -0.60566430 0.26906478 0 + -0.71397188 0.30871780 0 + 0.31008428 0.34675335 0 + 0.18018786 0.46204643 0 + -0.42663885 0.64723225 0 + 0.06143230 0.32491150 0 + 0.07736952 0.32183287 0 + 0.42814970 0.13445957 0 + -0.80250753 0.66878999 0 + 0.40142623 0.42516398 0 + 0.37084776 0.26407123 0 + -0.80774748 0.41485899 0 + 0.50163585 0.23934856 0 + 0.58238323 0.22842741 0 + -0.59136100 0.30230321 0 + -0.87037236 0.26941446 0 + -0.72086765 0.19676678 0 + 0.27778443 0.21792253 0 + 0.33240813 0.27349865 0 + -0.14092068 0.39247351 0 + -0.59759518 0.14790267 0 + -0.85581534 0.14513961 0 + -0.88912232 0.26896001 0 + 0.21345680 0.43611756 0 + -0.53467949 0.57901229 0 + 0.31686848 0.39705856 0 + -0.68121733 0.04209840 0 + -0.97586127 0.45964811 0 + 0.41457183 0.27141230 0 + 0.32751292 0.36780137 0 + -0.93209192 0.09362034 0 + 0.58395341 0.47147282 0 + -0.44437309 0.23010142 0 + 0.29109441 0.19365556 0 + -0.51080722 0.41496003 0 + -0.96597511 0.17931052 0 + 0.18741315 0.29747132 0 + 0.17965417 0.45175449 0 + -0.72689602 0.35728387 0 + -0.54339877 0.41012013 0 + -0.59823393 0.98701425 1 + -0.20194736 0.62101680 1 + 0.47146103 0.48221146 1 + -0.09821987 0.58755577 1 + -0.35657658 0.63709705 1 + 0.63881392 0.42112135 1 + 0.62980614 0.28146085 1 + -0.46223286 0.61661031 1 + -0.07331555 0.55821736 1 + -0.55405533 0.51253129 1 + -0.43761773 0.87811781 1 + -0.22237814 0.88850773 1 + 0.09346162 0.67310494 1 + 0.53174745 0.54372650 1 + 0.40207539 0.51638462 1 + 0.47555171 0.65056336 1 + -0.23383266 0.63642580 1 + -0.31579316 0.75031340 1 + -0.47351720 0.63854125 1 + 0.59239464 0.89256953 1 + -0.22605324 0.79789454 1 + -0.43995011 0.52099256 1 + -0.54645044 0.74577198 1 + 0.46404306 0.51065152 1 + -0.15194296 0.81218439 1 + 0.48536395 0.82018093 1 + 0.34725649 0.70813773 1 + 0.43897015 0.62817158 1 + -0.21415914 0.64363951 1 + 0.57380231 0.63713466 1 + 0.38717361 0.58578395 1 + 0.32038322 0.53529127 1 + -0.20781491 0.65132467 1 + -0.18651283 0.81754816 1 + 0.24752692 0.39081936 1 + 0.66049881 0.89919213 1 + -0.28658801 0.73375946 1 + -0.32588080 0.39865509 1 + -0.25204565 0.67358326 1 + 0.37259022 0.49785904 1 + -0.29096564 1.04372060 1 + -0.30469807 0.86858292 1 + -0.21389978 1.09317811 1 + -0.36830015 0.75639546 1 + -0.46928218 0.88775091 1 + 0.39350146 0.77975197 1 + -0.45639966 0.80523454 1 + 0.51128242 0.76606136 1 + 0.22550468 0.46451215 1 + 0.01462984 0.40190926 1 + -0.19172785 0.80943313 1 + 0.38323479 0.75601744 1 + 0.49791612 0.61334375 1 + 0.35335230 0.77324337 1 + -0.34722575 0.70177856 1 + 0.58380468 0.76357539 1 + -0.13727764 0.71246351 1 + 0.38827268 0.44977123 1 + -0.53172709 0.61934293 1 + -0.11684624 0.87851210 1 + 0.54335864 0.41174865 1 + -0.45399302 0.66512988 1 + -0.21913200 0.83484947 1 + 0.30485742 0.98028760 1 + 0.65676798 0.75766017 1 + 0.61420447 0.75039019 1 + -0.45809964 0.77968606 1 + -0.21617465 0.88626305 1 + -0.26016108 0.81008591 1 + 0.31884531 0.84517725 1 + -0.23727415 0.80178784 1 + 0.58310323 0.77709806 1 + 0.02841337 0.75792620 1 + -0.41840136 0.68041440 1 + 0.67412880 0.60245461 1 + -0.25278281 0.70526103 1 + 0.51609843 0.62092390 1 + 0.20392294 0.91641482 1 + -0.17207124 1.00884096 1 + 0.27274507 0.29346977 1 + 0.07634798 0.56222204 1 + -0.36653499 0.64831007 1 + 0.44290673 0.80087721 1 + -0.19976385 0.54295162 1 + -0.54075738 0.65293033 1 + -0.07060266 1.00296912 1 + 0.50715054 0.35045758 1 + -0.06048611 0.62982713 1 + 0.21532928 0.60260249 1 + 0.46809108 0.87182416 1 + -0.29888511 0.73669866 1 + 0.86129620 0.47289330 1 + 0.70120877 0.74572893 1 + -0.11342797 0.60067099 1 + 0.31234354 0.90756345 1 + -0.12172541 0.84112851 1 + 0.36867857 0.37052586 1 + 0.57311489 0.40949740 1 + -0.25841225 0.67192335 1 + 0.30937186 0.50823318 1 + 0.43319338 0.77016967 1 + -0.30448035 0.57820106 1 + 0.44276338 0.58023403 1 + -0.19442057 0.89876808 1 + -0.06105237 0.74184946 1 + 0.07619347 0.35386246 1 + 0.85826993 0.95819523 1 + 0.37039200 0.72342401 1 + 0.51481515 0.76203996 1 + 0.43127521 0.54259166 1 + 0.42286091 0.65242185 1 + 0.29815001 0.93453682 1 + 0.37128253 0.70089181 1 + -0.51528729 0.76473490 1 + 0.38525783 0.65528189 1 + -0.34825368 0.50529981 1 + 0.68510504 0.78067440 1 + -0.36528923 0.45703265 1 + -0.40903577 0.74230433 1 + 0.43574387 0.44689789 1 + 0.26887846 0.44559230 1 + -0.49254862 1.01443372 1 + 0.07615960 0.63795180 1 + 0.49226224 0.46876241 1 + -0.40249641 0.71301084 1 diff --git a/toolbox/mci/demo-gradients/mci_compare_forward.m b/toolbox/mci/demo-gradients/mci_compare_forward.m new file mode 100644 index 00000000..6ef8bde4 --- /dev/null +++ b/toolbox/mci/demo-gradients/mci_compare_forward.m @@ -0,0 +1,96 @@ +function [els_sun,els_ode,els_spm] = mci_compare_forward (model) +% Compare integration methods +% FORMAT [els_sun,els_ode,els_spm] = mci_compare_forward (model) +% +% model 'phase', 'nmm-r2p2' +% +% Run integration 9 times - compare speed and accuracy +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_compare_forward.m 6548 2015-09-11 12:39:47Z will $ + +[P,M,U,Y] = mci_compare_setup (model); + +M.reltol=1e-2; +M.abstol=1e-4; + +M = spm_mci_minit (M); +if isstruct(M.pC) + pC=full(diag(spm_vec(M.pC))); +else + pC = M.pC; +end + +UI.u=U'; +UI.dt=M.T/M.N; + +Nsamp=9; +for s=1:Nsamp, + % New point + P=spm_normrnd(M.vpE,pC,1); + + % Parameters in reduced space + Pr = M.V'*(P-M.vpE); + + M.int='sundials'; + tic; + y = spm_mci_fwd (P,M,U); + ysun(:,s)=y(:,1); + els_sun(s)=toc; + + M.int='ode15'; + tic; + y = spm_mci_fwd (P,M,U); + yode(:,s)=y(:,1); + els_ode(s)=toc; + + % DCM for fMRI uses spm_int + % DCM for ERP uses spm_int_L + tic; + y=spm_int_L(P,M,UI); + yspm(:,s)=y(:,1); + els_spm(s)=toc; + +end + +hs=figure; +set(hs,'Name','First Time Series'); +lw=2; +rN=ceil(sqrt(Nsamp)); +for s=1:Nsamp, + subplot(rN,rN,s); + plot(ysun(:,s),'k'); + hold on + grid on + plot(yode(:,s),'b'); + plot(yspm(:,s),'r'); +end +legend('Sundials','ODE15','spm-int-L'); + +hs=figure; +set(hs,'Name','Integration Speed'); +k=1; lw=2; +plot(els_ode,els_sun,'kx','MarkerSize',10); +hold on +mo=min(els_ode); +ma=max(els_ode); +plot([mo ma],[mo ma],'k','LineWidth',lw); +set(gca,'FontSize',18); +grid on +xlabel('ODE15'); +ylabel('Sundials'); + +hs=figure; +set(hs,'Name','Integration Speed'); +k=1; lw=2; +plot(els_spm,els_sun,'kx','MarkerSize',10); +hold on +mo=min(els_spm); +ma=max(els_spm); +plot([mo ma],[mo ma],'k','LineWidth',lw); +set(gca,'FontSize',18); +grid on +xlabel('spm-int-L'); +ylabel('Sundials'); diff --git a/toolbox/mci/demo-gradients/mci_compare_gradients.m b/toolbox/mci/demo-gradients/mci_compare_gradients.m new file mode 100644 index 00000000..d777c1c6 --- /dev/null +++ b/toolbox/mci/demo-gradients/mci_compare_gradients.m @@ -0,0 +1,196 @@ +function [els,names] = mci_compare_gradients (model,cost,methods) +% Compare methods for gradient computation +% FORMAT [els,names] = mci_compare_gradients (model,cost,methods) +% +% model 'phase', 'nmm-r2p2' +% cost 'loglike', 'spm_mci_joint' (default) +% methods vector of integers indicating which methods to +% compare eg. [1,2,3,4,5] (default) for 1. SensMat, +% 2. SensSun, 3. AdjMat, 4. AdjSun, 5. FD +% +% els Computation times +% names Names of compared methods +% +% Note: 4. AdjSun may not work for nmm2-r2p2. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_compare_gradients.m 6548 2015-09-11 12:39:47Z will $ + +if nargin < 2 | isempty(cost) + cost='spm_mci_joint'; +end + +if nargin < 3 | isempty(methods) + methods=[1:5]; +end + +for i=1:5, + if length(intersect(methods,i))>0 + m(i)=1; + else + m(i)=0; + end +end + +% Set dp for Finite Differences +sdp=1e-1; + +[P,M,U,Y,ind] = mci_compare_setup (model); + +mci_plot_outputs(M,Y); + +M.reltol=1e-2; +M.abstol=1e-4; + +M = spm_mci_minit (M); +if isstruct(M.pC) + pC=full(diag(spm_vec(M.pC))); +else + pC = M.pC; +end + +% Evalute gradients at new point +P=spm_normrnd(M.vpE,pC,1); + +% Parameters in reduced space +Pr = M.V'*(P-M.vpE); + +names={'SensMat','SensSun','AdjMat','AdjSun','FD'}; +names=names(methods); +cm=1; + +if strcmp(cost,'loglike') + + if m(1) + M.int='ode15'; + tic; + [dLdp,tmp,st] = spm_mci_glike_deriv (P,M,U,Y); + els(cm)=toc; + cm=cm+1; + end + + if m(2) + M.int='sundials'; + tic; + [dLdp_sun,tmp,st] = spm_mci_glike_deriv (P,M,U,Y); + els(cm)=toc; + cm=cm+1; + end + + if m(3) + M.adjlike=1; + tic; + [dLdp_adj] = spm_mci_adjoint (Pr,M,U,Y); + els(cm)=toc; + cm=cm+1; + end + + if m(4) + %M.backint=1; + tic; + [dLdp_adj_sun] = spm_mci_adjoint_sun (Pr,M,U,Y); + els(cm)=toc; + cm=cm+1; + end + + if m(5) + tic; + P0=P; + L0=spm_mci_glike(P0,M,U,Y); + + for i=1:M.Np, + Pd=P0; + dp=sdp*Pd(i); + Pd(i)=Pd(i)+dp; + Ld = spm_mci_glike (Pd,M,U,Y); + dLdp_finite(i)=(Ld-L0)/dp; + end + els(cm)=toc; + end + +else + if m(1) + tic; + M.int='ode15'; + [dLdp,tmp,st] = spm_mci_joint_grad (Pr,M,U,Y); + els(cm)=toc; + cm=cm+1; + end + + if m(2) + tic; + M.int='sundials'; + [dLdp_sun,tmp,st] = spm_mci_joint_grad (Pr,M,U,Y); + els(cm)=toc; + cm=cm+1; + end + + if m(3) + tic; + [dLdp_adj] = spm_mci_adjoint (Pr,M,U,Y); + els(cm)=toc; + cm=cm+1; + end + + if m(4) + tic; + [dLdp_adj_sun] = spm_mci_adjoint_sun (Pr,M,U,Y); + els(cm)=toc; + cm=cm+1; + end + + if m(5) + tic; + P0=Pr; + L0=spm_mci_joint(P0,M,U,Y); + for i=1:M.Np, + Pd=P0; + dp=sdp*Pd(i); + Pd(i)=Pd(i)+dp; + Ld = spm_mci_joint (Pd,M,U,Y); + dLdp_finite(i)=(Ld-L0)/dp; + end + els(cm)=toc; + end +end + +if st==-1 + disp('Problem with integration'); + return +end + +figure; +if m(1) plot(dLdp(ind)); end +hold on +if m(2) plot(dLdp_sun(ind),'c'); end +if m(3) plot(dLdp_adj(ind),'r'); end +if m(4) plot(dLdp_adj_sun(ind),'m'); end +if m(5) plot(dLdp_finite(ind),'k'); end +xlabel('p'); +ylabel('dLdp'); +grid on +legend(names); + +figure +bar(els,'k'); +grid on +ylabel('Seconds'); +set(gca,'XTickLabel',names); + +disp(' '); +if isfield(M,'dfdx') + disp('State Jacobian M.dfdx is specified for this model'); +else + disp('State Jacobian M.dfdx not specified for this model'); +end +if isfield(M,'dfdp') + disp('Parameter Jacobian M.dfdp is specified for this model'); +else + disp('Parameter Jacobian M.dfdp not specified for this model'); +end + +disp(' '); +disp('SensMat, AdjMat and AdjSun will use'); +disp('M.dfdx and M.dfdp if these fields are specified.'); diff --git a/toolbox/mci/demo-gradients/mci_compare_jacobians.m b/toolbox/mci/demo-gradients/mci_compare_jacobians.m new file mode 100644 index 00000000..eef86b81 --- /dev/null +++ b/toolbox/mci/demo-gradients/mci_compare_jacobians.m @@ -0,0 +1,62 @@ +function [Fx,Fp,FxFD,FpFD] = mci_compare_jacobians (model) +% Compare user supplied and finite difference methods +% FORMAT [Fx,Fp,FxFD,FpFD] = mci_compare_jacobians (model) +% +% model 'phase' +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_compare_jacobians.m 6548 2015-09-11 12:39:47Z will $ + +switch model, + case 'phase', + d=3; + disp(sprintf('Weakly coupled oscillator network : d=%d regions',d)); + [P,M,u,Y] = mci_phase_init(d); + M.dfdx='mci_phase_dfdx'; + M.dfdp='mci_phase_dfdp'; + + otherwise + disp('Unknown model'); + return +end + +M.x0=randn(M.n,1); + +disp('User supplied Jacobian (blue)'); +disp('Finite difference Jacobian (red)'); +disp(' '); + +Fx = feval(M.dfdx,M.x0,u,P,M); + +FxFD = spm_diff(M.f,M.x0,u,P,M,1); + +figure +for i=1:M.n, + subplot(M.n,1,i); + plot(Fx(i,:)); + hold on + plot(FxFD(i,:),'r'); + ylabel(sprintf('df(%d)/dx(j)',i)); + grid on +end +xlabel('j'); +disp('Percentage discrepancy in dfdx:'); +100*sum(abs(Fx(:)-FxFD(:)))/sum(abs(FxFD(:))) + +Fp = feval(M.dfdp,M.x0,u,P,M); +FpFD = spm_diff(M.f,M.x0,u,P,M,3); + +figure +for i=1:M.n, + subplot(M.n,1,i); + plot(Fp(i,:)); + hold on + plot(FpFD(i,:),'r'); + ylabel(sprintf('df(%d)/dp(j)',i)); + grid on +end +xlabel('j'); +disp('Percentage discrepancy in dfdp:'); +100*sum(abs(Fp(:)-FpFD(:)))/sum(abs(FpFD(:))) diff --git a/toolbox/mci/demo-gradients/mci_compare_sensitivities.m b/toolbox/mci/demo-gradients/mci_compare_sensitivities.m new file mode 100644 index 00000000..51a56698 --- /dev/null +++ b/toolbox/mci/demo-gradients/mci_compare_sensitivities.m @@ -0,0 +1,52 @@ +function [] = mci_compare_sensitivities (model,pars) +% Compare methods for sensitivity computation +% FORMAT [] = mci_compare_sensitivities (model,pars) +% +% model 'phase', 'nmm-r2p2' +% pars vector indicating which sensitivities to plot +% eg. [1,2,..,Np] (default) for all parameters +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_compare_sensitivities.m 6548 2015-09-11 12:39:47Z will $ + +[P,M,U,Y] = mci_compare_setup (model); + +if nargin < 2 | isempty(pars) + pars=[1:M.Np]; +end +Np=length(pars); + +disp(' '); +disp('Plot sensitivity to parameter changes as computed by:'); +disp('(1) Forward equations based on Matlab''s ODE suite (red)'); +disp('(2) Forward equations based on Sundials (blue)'); + +[G,sy_m,st] = spm_mci_sens (P,M,U); + +[G,sy,st] = spm_mci_sens_sun (P,M,U); + +if st==-1 + disp('Problem with integration'); + return +end + +mci_plot_outputs(M,G); + +hs=figure; +set(hs,'Name','Sensitivities'); +k=1; lw=2; +for i=1:M.l, + for p=1:Np, + j=pars(p); + subplot(M.l,Np,k); + plot(M.t,squeeze(sy(:,i,j)),'LineWidth',lw); + hold on + plot(M.t,squeeze(sy_m(:,i,j)),'r','LineWidth',lw); + grid on + title (sprintf('dy(%d)/dp(%d)',i,j)); + xlabel('Time'); + k=k+1; + end +end diff --git a/toolbox/mci/demo-group/mci_demo_mfx_lds.m b/toolbox/mci/demo-group/mci_demo_mfx_lds.m new file mode 100644 index 00000000..32cd752e --- /dev/null +++ b/toolbox/mci/demo-group/mci_demo_mfx_lds.m @@ -0,0 +1,86 @@ + +clear all +close all + +disp('Data from multiple subjects'); + +disp('Estimate mixed effects using Langevin Monte Carlo'); +disp('Using LDS model with constrained connectivity'); + +lds.model='forward'; + +% Number of dynamical states e.g. brain areas +d=4; + +% Observation noise +lds.sd=0.1; + +% Prior over initial states +lds.R.pE=linspace(3,1.5,d)'; +lds.R.pC=0.5^2*eye(d); + +% Number of subjects +lds.Nsub=3; + +% Number of observations per subject +lds.Nobs=5; + +lds.init_par='random'; +lds.flow_par='fixed'; + +% Generate group data +[lds.pinit,lds.pflow,lds.names,M,U,Y] = mci_lds_group_data (lds); + +% Assign init/flow as random/fixed effects +assign.init_par='random'; +assign.flow_par='fixed'; +assign.out_par='known'; + +MCI.assign=assign; + +MCI.fixed.pE=M{1}.pE; +MCI.fixed.pC=M{1}.pC; + +% Initialisation +i0=mci_interp_init(Y,M{1}); +a0=[]; +if strcmp(assign.init_par,'random') + MCI.pinit0=i0; +else + MCI.pinit0=mean(i0,2); +end +if strcmp(assign.flow_par,'random') + MCI.pflow0=spm_vec(M{1}.pE)*ones(1,lds.Nsub); + if ~isempty(a0), MCI.pflow0(1:d,:)=a0; end +else + MCI.pflow0=spm_vec(M{1}.pE); + if ~isempty(a0), MCI.pflow0(1:d,:)=mean(a0,2); end +end +MCI.pout0=[]; + + +MCI.M=M; MCI.U=U; MCI.Y=Y; +MCI.update_obs_noise=1; +MCI.verbose=1; +MCI.total_its=16; +MCI.rinit=0.25; + +tic; +MCI = spm_mci_mfx_dynamic (MCI); +toc + +rmse=mci_lds_plot_params (MCI,lds); + +for n=1:lds.Nsub, + mci_lds_plot_fit (MCI,lds,n,1); +end + +disp('True dynamics:'); +[f,Atrue] = mci_lds_fx (lds.pinit(:,1),U{1},lds.pflow,M{1}); +disp(Atrue); + +disp('Estimated dynamics:'); +[f,Aest] = mci_lds_fx (MCI.pinit,U{1},MCI.pflow,M{1}); +disp(Aest); + +mci_plot_noiseSD (MCI.Ce,MCI.post_ind); diff --git a/toolbox/mci/demo-group/mci_demo_rfx_linear.m b/toolbox/mci/demo-group/mci_demo_rfx_linear.m new file mode 100644 index 00000000..b1ab7108 --- /dev/null +++ b/toolbox/mci/demo-group/mci_demo_rfx_linear.m @@ -0,0 +1,129 @@ + +clear all +close all + +disp('Linear RFX example'); + +% Number of subjects +N=8; + +% Design matrix +des='dct'; +%des='quadratic'; +%des='linear-offset'; + +% Time points per subject +Nobs=20; + +% Observation noise precision +sd=0.2; +lambda=1/(sd^2); + +% Population-level parameters +[M{1},U{1}] = mci_linear_struct (Nobs,lambda,des); +w_pop=spm_normrnd(M{1}.pE,M{1}.pC,1); + +% Subject parameters and data +for n=1:N, + [M{n},U{n},Xfull] = mci_linear_struct (Nobs,lambda,des); + w_true(:,n) = spm_normrnd(w_pop,M{n}.pC,1); + Y{n}.y = U{n}.X*w_true(:,n)+sqrt(M{n}.Ce)*randn(Nobs,1); +end + +% PEB analysis +do_PEB=1; +if do_PEB + disp('Parametric Empirical Bayes ...'); + P{1}.X=[];y=[]; + for n=1:N, + P{1}.X=blkdiag(P{1}.X,U{n}.X); + y=[y;Y{n}.y]; + end + Np=length(M{1}.pE); + Nx=size(P{1}.X,1); + P{1}.C{1}=eye(Nx); + P{2}.X=kron(ones(N,1),eye(Np)); + % Prior variance components for each parameter + for j=1:Np, + z=zeros(Np,Np); + z(j,j)=1; + P{2}.C{j}=kron(eye(N),z); + end + tic; + [C,P,F] = spm_PEB(y,P); + toc + m_PEB=C{3}.E; % Population - posterior mean + C_PEB=C{3}.C; % Population - posterior covariance + h_PEB=C{2}.h; % Estimate prior vars + w_PEB=reshape(C{2}.E,Np,N); % Subject effects + names={'True','MCI','PEB'}; +else + names={'True','MCI'}; +end + +% MCI +disp('MCI ...'); +MCI.verbose=0; +MCI.M=M; MCI.U=U; MCI.Y=Y; +MCI.update_obs_noise=1; + +% Assume all variables are random effects +Nrand=length(M{1}.pE); +S.prior.P=Nrand; +S.prior.a=Nrand/2; +S.prior.B=eye(Nrand); +S.prior.beta=1; +S.prior.m=zeros(Nrand,1); +S.N=length(M); +MCI.S=S; + +tic; +MCI = spm_mci_mfx (MCI); +toc + +% Population-level estimates +dist{1}.P=MCI.sm; +dist{1}.Ep=MCI.sm_mean; +dist{1}.ind=MCI.post_ind; +dist{1}.type='sample'; +dist{1}.color='k'; +dist{1}.names=U{1}.names; +mci_plot_dist_multi (dist,'MCI: Pop-Level',w_pop); + +% Subject-level estimates +h=figure; +set(h,'Name',sprintf('Subject Level')); +for j=1:Np, + subplot(Np,1,j); + plot(squeeze(MCI.sw(j,:,:))'); + grid on + xlabel('RFX iteration'); + ylabel(sprintf('w(%d)',j)); +end + +if do_PEB + dist{1}.Ep=m_PEB; + dist{1}.Cp=C_PEB; + dist{1}.type='Gaussian'; + dist{1}.color='b'; + dist{1}.names=U{1}.names; + mci_plot_dist_multi (dist,'PEB: Pop-Level',w_pop); +end + +mci_plot_noiseSD (MCI.Ce,MCI.post_ind); + +plot_model_fits=0; +if plot_model_fits + % Plot model fits + Nplot=min(N,8); + for n=1:Nplot, + yt{1}=Xfull*w_true(:,n); + yt{2}=Xfull*MCI.sw_mean(:,n); + if do_PEB + yt{3}=Xfull*w_PEB(:,n); + end + h=figure; + set(h,'Name',sprintf('Subject %d',n)); + mci_linear_plot_fit (M{n},Y{n}.y,yt,names); + end +end diff --git a/toolbox/mci/demo-group/mci_demo_rfx_logistic.m b/toolbox/mci/demo-group/mci_demo_rfx_logistic.m new file mode 100644 index 00000000..c4c7ee5f --- /dev/null +++ b/toolbox/mci/demo-group/mci_demo_rfx_logistic.m @@ -0,0 +1,77 @@ + +clear all +close all + +disp('Logistic RFX example'); + +% Number of subjects +N=16; + +[M1,U1] = mci_logistic_struct ('dct'); +w_pop=[0.2,8,6]'; +Np=length(w_pop); + +% [g,y]=mci_logistic_gen(P1,M1,U1); +% figure;plot(g); +% figure;imagesc(U1.X); +% return + +w_true=spm_normrnd(w_pop,0.001*eye(3),N); + +% Subject parameters and data +for n=1:N, + M{n}=M1; + U{n}=U1; + [tmp,Y{n}.y] = mci_logistic_gen(w_true(:,n),M1,U1); +end + +% MCI +disp('MCI ...'); +MCI.verbose=0; +MCI.M=M; MCI.U=U; MCI.Y=Y; + +MCI.total_its=1024; +MCI.rinit=0.25; + +MCI.inference='lgv'; +%MCI.inference='amc'; + +% Assume all variables are random effects +Nrand=length(M{1}.pE); +S.prior.P=Nrand; +S.prior.a=Nrand/2; +S.prior.B=eye(Nrand); +S.prior.beta=1; +S.prior.m=zeros(Nrand,1); +S.N=length(M); +MCI.S=S; + +tic; +MCI = spm_mci_mfx (MCI); +toc + +% Population-level estimates +dist{1}.P=MCI.sm; +dist{1}.Ep=MCI.sm_mean; +dist{1}.ind=MCI.post_ind; +dist{1}.type='sample'; +dist{1}.color='k'; +dist{1}.names=U{1}.names; +mci_plot_dist_multi (dist,'MCI: Pop-Level',w_pop); +grid on + +% Subject-level estimates +h=figure; +set(h,'Name',sprintf('Subject Level')); +for j=1:Np, + subplot(Np,1,j); + plot(squeeze(MCI.sw(j,:,:))'); + grid on + xlabel('RFX iteration'); + ylabel(sprintf('w(%d)',j)); +end + +SE=(MCI.sw_mean-w_true).^2; +RMSE=sqrt(mean(SE,2)); +aRMSE=mean(RMSE); +disp(sprintf('Average RMSE over parameters=%1.2f',aRMSE)); diff --git a/toolbox/mci/demo-group/mci_demo_rfx_nmm.m b/toolbox/mci/demo-group/mci_demo_rfx_nmm.m new file mode 100644 index 00000000..5f4f7978 --- /dev/null +++ b/toolbox/mci/demo-group/mci_demo_rfx_nmm.m @@ -0,0 +1,99 @@ + +clear all +close all + +disp('Random Effects demo with'); +disp('two-region Neural Mass Models'); +disp(' '); +disp('Initial conditions are known'); +disp('Flow parameters are treated as random effects'); + +% Backward connection ? +back=1; + +% Observation noise SD +sd=0.01; + +% Number of subjects +N=3; + +% Number of parameters +Np=2; + +m=[1,1]'; +C=0.1^2*eye(2); +w_true = spm_normrnd(m,C,N); +for n=1:N, + [M{n},U{n}] = mci_nmm_struct(back,sd,Np); + P=w_true(:,n); + Y{n}.y = mci_nmm_gen(M{n},U{n},P); +end + +% Assign init, flow and output parameters +assign.init_par='known'; +assign.flow_par='random'; +assign.out_par='known'; +MCI.pout0=[]; % there are no 'output' parameters +MCI.assign=assign; + +MCI.M=M; MCI.U=U; MCI.Y=Y; +MCI.verbose=1; +MCI.total_its=16; +MCI.rinit=0.25; + +S.prior.P=Np; +S.prior.a=Np/2; +S.prior.B=M{1}.pC; +S.prior.beta=1; +S.prior.m=zeros(Np,1); +S.N=N; +MCI.S=S; + +tic; +MCI = spm_mci_mfx_dynamic (MCI); +toc + +disp('Mean true params:'); +mwt=mean(w_true,2) + +disp('MCI second level posterior:'); +MCI.sm_mean + +Np=M{1}.Npflow; +for j=1:Np, + h=figure; + set(h,'Name',sprintf('w(%d)',j)); + + min_w=min(w_true(j,:)); + max_w=max(w_true(j,:)); + + plot(w_true(j,:),MCI.sw_mean(j,:),'kx','MarkerSize',10); + hold on + plot([min_w max_w],[min_w max_w],'k-','LineWidth',2); + set(gca,'FontSize',18); + xlabel('True'); + ylabel('Estimated'); + grid on +end + +e=MCI.sw_mean-w_true; +sse2=trace(e'*e); +disp(sprintf('Final first level SSE=%1.2f',sse2)); + +figure; +plot(MCI.sm'); +grid on +xlabel('RFX iteration'); +ylabel('Population Level Parameters'); + +h=figure; +set(h,'Name',sprintf('Subject Level')); +for j=1:Np, + subplot(Np,1,j); + plot(squeeze(MCI.sw(j,:,:))'); + grid on + xlabel('RFX iteration'); + ylabel(sprintf('w(%d)',j)); +end + + diff --git a/toolbox/mci/demo-group/mci_demo_rfx_rphase.m b/toolbox/mci/demo-group/mci_demo_rfx_rphase.m new file mode 100644 index 00000000..5994ad59 --- /dev/null +++ b/toolbox/mci/demo-group/mci_demo_rfx_rphase.m @@ -0,0 +1,114 @@ + +clear all +close all + +disp('Random Effects demo with'); +disp('Phase Coupling Models'); +disp(' '); +disp('Initial conditions are known'); +disp('Flow parameters are treated as random effects'); + +disp('Synthetic data from d-region weakly coupled oscillator'); +disp('model with reduced connectivity'); + +% Dimension of state space +d=3; + +conn(1).regions=[1,2]; +conn(1).name='vis2mtl'; +conn(2).regions=[3,2]; +conn(2).name='vis2ifg'; + +[M1,U1] = mci_rphase_struct(d,conn); +M1.freq=6; +M1.Npflow=M1.Np; +M1.Npout=0; +M1.x0=[-pi/2,0,pi/2]'; + +% Observation noise SD +sd=0.01; + +% Number of subjects +N=12; +m=[-0.6,-0.6,0,0]; +Np=length(m); +C=0.1^2*eye(Np); +w_true = spm_normrnd(m,C,N); +for n=1:N, + M{n}=M1;U{n}=U1; + wn=w_true(:,n); + pars.aconn=[wn(1), wn(2)]; + pars.bconn=[wn(3), wn(4)]'; + %pars.bconn=[0,0]'; + P=spm_vec(pars); + Y{n}.y = mci_rphase_gen(P,M{n},U{n}); +end + +% Assign init, flow and output parameters +assign.init_par='known'; +assign.flow_par='random'; +assign.out_par='known'; +MCI.pout0=[]; % there are no 'output' parameters +MCI.assign=assign; + +MCI.M=M; MCI.U=U; MCI.Y=Y; +MCI.verbose=1; +MCI.total_its=16; +MCI.rinit=0.25; + +S.prior.P=Np; +S.prior.a=Np/2; +S.prior.B=M{1}.pC; +S.prior.beta=1; +S.prior.m=zeros(Np,1); +S.N=N; +MCI.S=S; + +tic; +MCI = spm_mci_mfx_dynamic (MCI); +toc + +disp('Mean true params:'); +mwt=mean(w_true,2) + +disp('MCI second level posterior:'); +MCI.sm_mean + +Np=M{1}.Npflow; +for j=1:Np, + h=figure; + set(h,'Name',sprintf('w(%d)',j)); + + min_w=min(w_true(j,:)); + max_w=max(w_true(j,:)); + + plot(w_true(j,:),MCI.sw_mean(j,:),'kx','MarkerSize',10); + hold on + plot([min_w max_w],[min_w max_w],'k-','LineWidth',2); + set(gca,'FontSize',18); + xlabel('True'); + ylabel('Estimated'); + grid on +end + +e=MCI.sw_mean-w_true; +sse2=trace(e'*e); +disp(sprintf('Final first level SSE=%1.2f',sse2)); + +figure; +plot(MCI.sm'); +grid on +xlabel('RFX iteration'); +ylabel('Population Level Parameters'); + +h=figure; +set(h,'Name',sprintf('Subject Level')); +for j=1:Np, + subplot(Np,1,j); + plot(squeeze(MCI.sw(j,:,:))'); + grid on + xlabel('RFX iteration'); + ylabel(sprintf('w(%d)',j)); +end + + diff --git a/toolbox/mci/demo-models/mci_demo_approach.m b/toolbox/mci/demo-models/mci_demo_approach.m new file mode 100644 index 00000000..58147945 --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_approach.m @@ -0,0 +1,71 @@ + +clear all +close all + +Nobs=40; + +[M,U] = mci_approach_struct (Nobs); + +rand_params=1; +if rand_params + P = spm_normrnd(M.pE,M.pC,1); +else + V = 30; + tau = 8; + P = [log(V),log(tau)]'; +end + +yhat = mci_approach_gen (P,M,U); +Y = yhat + sqrt(M.Ce)*randn(Nobs,1); + +% Check analytic gradients using finite differences +%dLdp = approach_deriv (P,M,U,Y); +%dLdp = spm_diff(M.L,P,M,U,Y,1); + +% figure +% plot(U.X,Y); +% hold on +% plot(U.X,yhat,'r'); + +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.verbose=0; +mcmc.maxits=1024; + +post = spm_mci_post (mcmc,M,U,Y,P); + +disp('Prior Mean (circle):'); +disp(M.pE); + +disp('Posterior Mean from MCI (cross)'); +disp(post.Ep) + +disp('True (plus)'); +disp(P) + +% Plot posterior surface +S.Nbins=100; +S.pxy(1,:)=linspace(2,4,S.Nbins); +S.pxy(2,:)=linspace(0.5,3.5,S.Nbins); +S.param{1}='P(1)'; +S.param{2}='P(2)'; +S.name={'log V_a','log \tau'}; +%[L,S] = mci_plot_surface (P,M,U,Y,S,'prior'); +%[L,S] = mci_plot_surface (P,M,U,Y,S,'like'); +[L,S] = mci_plot_surface (P,M,U,Y,S,'post'); +hold on +ms=10; +plot(M.pE(1),M.pE(2),'wo','MarkerSize',ms); +plot(post.Ep(1),post.Ep(2),'wx','MarkerSize',ms); +plot(P(1),P(2),'w+','MarkerSize',ms); +j=post.ind; +plot(post.P(1,j),post.P(2,j),'w.'); + +diag.essplot=1 +diag.ind=j; +mess=spm_mci_diag(post,diag); + +stats = spm_mci_mvnpost (post,'ESS') +stats = spm_mci_mvnpost (post,'thinning') + diff --git a/toolbox/mci/demo-models/mci_demo_discount.m b/toolbox/mci/demo-models/mci_demo_discount.m new file mode 100644 index 00000000..512fda5f --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_discount.m @@ -0,0 +1,66 @@ + +clear all +close all + +disp('Temporal discounting model'); + +% Parameters for first level +Nobs=200; + +[M,U] = mci_discount_struct (Nobs); + +% Generate Data +%w_true = spm_normrnd(M.pE,M.pC,1); +w_true = [0.25, 1.5]'; +w_true = log(w_true); +[g,Y] = mci_discount_gen (w_true,M,U); + +[a,v1,v2,k] = mci_discount_act (w_true,M,U); + +t=sort(U.t1); +r=mean(U.r1); +figure;plot(t,r./(1+k*t),'k.'); +set(gca,'FontSize',16); +xlabel('Delay, Weeks'); + +t=sort(U.t2); +r=mean(U.r2); +hold on +plot(t,r./(1+k*t),'r.'); +ylabel('Mean Reward, £'); +grid on + +disp(sprintf('Average prob of choosing first option = %1.2f',mean(g))); + +%mcmc.inference='amc'; +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.verbose=0; +mcmc.maxits=1024; + +post = spm_mci_post (mcmc,M,U,Y,w_true); +disp('Posterior Mean from MCI'); +disp(post.Ep) + +plot_post=1; +if plot_post + % Plot posterior surface + S.Nbins=50; + r=2; + S.pxy(1,:)=linspace(-r*w_true(1),r*w_true(1),S.Nbins); + S.pxy(2,:)=linspace(-0.5*r*w_true(2),1.5*r*w_true(2),S.Nbins); + S.param{1}='P(1)'; + S.param{2}='P(2)'; + S.name={'log k','log \beta'}; + [L,S] = mci_plot_surface (w_true,M,U,Y,S,'post'); + hold on + ms=10; + plot(M.pE(1),M.pE(2),'wo','MarkerSize',ms); + plot(post.Ep(1),post.Ep(2),'wx','MarkerSize',ms); + plot(w_true(1),w_true(2),'w+','MarkerSize',ms); +end + +stats = spm_mci_mvnpost (post,'ESS') +stats = spm_mci_mvnpost (post,'thinning') + diff --git a/toolbox/mci/demo-models/mci_demo_growth.m b/toolbox/mci/demo-models/mci_demo_growth.m new file mode 100644 index 00000000..034f2006 --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_growth.m @@ -0,0 +1,45 @@ + +clear all +close all + +Nobs=40; + +[M,U] = mci_pb_struct (Nobs); +M.Ce=4; + +P = spm_normrnd(M.pE,M.pC,1); + +yhat = mci_pb_gen (P,M,U); +Y = yhat + sqrt(M.Ce)*randn(Nobs,1); + +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.verbose=0; +mcmc.maxits=2048; + +post = spm_mci_post (mcmc,M,U,Y,P); + +disp('Prior Mean, Posterior Mean, True:'); +disp([M.pE,post.Ep,P]); + +y_true = mci_pb_gen (P,M,U); +y_prior = mci_pb_gen (M.pE,M,U); +y_post = mci_pb_gen (post.Ep,M,U); + +figure +plot(U.X,y_true,'b'); +hold on +plot(U.X,y_post,'r'); +plot(U.X,y_prior,'k'); +grid on +plot(U.X,Y,'.'); + +j=post.ind; +diag.essplot=1; +diag.ind=j; +mess=spm_mci_diag(post,diag); + +disp('p-value for test of multivariate normality:'); +p = spm_mci_mvntest(post.P(:,j)',mess) + diff --git a/toolbox/mci/demo-models/mci_demo_lds.m b/toolbox/mci/demo-models/mci_demo_lds.m new file mode 100644 index 00000000..6cce2fa1 --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_lds.m @@ -0,0 +1,95 @@ + +clear all +close all + +disp('Synthetic data from d-region LDS'); +disp('Model has constrained connectivity'); +disp(' '); + +% Number of regions +M.d=4; + +% Between region connectivity [to, from] +M.name='forward'; +%M.name='backward'; +%M.name='bidirectional'; + +% Observation noise SD +M.sd=0.1; + +% Initial states +M.R=linspace(3,1.5,M.d)'; +M.drop=0.5; +%M.t=[1:400]'/4; +M.t=[1:25]'/0.25; + +[M,U,names] = mci_lds_struct (M); + +Pt = [-0.04,-0.01,-0.005,-0.01,0.01,0.01,0.01]'; +P = mci_lds_par2lat (Pt,M); + +%P = lds_params (M,U); +turn_off_connectivity=0; +if turn_off_connectivity + P(M.d+1:end)=0; +end +Y = mci_lds_gen (M,U,P); + +figure; +plot(M.t,Y); + +%mcmc.inference='amc'; +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.maxits=1024; +mcmc.verbose=0; + +post = spm_mci_post (mcmc,M,U,Y); + +disp('True dynamics:'); +[f,A] = mci_lds_fx (M.x0,U,P,M); +disp(A); + +disp('Estimated dynamics:'); +[f,A] = mci_lds_fx (M.x0,U,post.Ep,M); +disp(A); + +plot_prior=0; +if strcmp(mcmc.inference,'langevin') + scale=100; + dist{1}=post; + dist{1}.color='k'; + dist{1}.names=names; + S=size(dist{1}.P,2); + for s=1:S, + Pt_post(:,s)=mci_lds_lat2par (dist{1}.P(:,s),M); + end + dist{1}.P=Pt_post*scale; + + if plot_prior + Ns=1000; + P=spm_normrnd(M.pE,M.pC,Ns); + dist{2}.ind=[1:Ns]; + dist{2}.color='b'; + dist{2}.names=dist{1}.names; + dist{2}.type='sample'; + for s=1:Ns, + Pt_prior(:,s)=mci_lds_lat2par (P(:,s),M); + end + dist{2}.P=Pt_prior*scale; + end + mci_plot_dist_multi (dist,'Flow',Pt*scale); +end + +switch mcmc.inference + case 'langevin', + ind=[1:size(post.Ce,3)]; + mci_plot_noiseSD (post.Ce,ind); + case 'vl', + disp('Error SD:'); + disp(sqrt(diag(post.Ce))); +end + +stats = spm_mci_mvnpost (post,'ESS') +stats = spm_mci_mvnpost (post,'thinning') diff --git a/toolbox/mci/demo-models/mci_demo_linear.m b/toolbox/mci/demo-models/mci_demo_linear.m new file mode 100644 index 00000000..ba5ded3a --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_linear.m @@ -0,0 +1,79 @@ + +clear all +close all + +disp('Linear Regression example'); + +% Design type +%des='linear-offset'; +%des='quadratic-offset'; +des='dct'; +%des='quadratic'; + +% Parameters for first level +Nobs=20; +sd=0.2; +lambda=1/(sd^2); + +[M,U,Xfull] = mci_linear_struct (Nobs,lambda,des); + +% Generate Data +w_true = spm_normrnd(M.pE,M.pC,1); +Y = U.X*w_true+sqrt(M.Ce)*randn(Nobs,1); + +%mcmc.inference='amc'; +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.verbose=0; +mcmc.maxits=1024; + +post = spm_mci_post (mcmc,M,U,Y,w_true); +disp('Posterior Mean from MCI'); +disp(post.Ep) + +disp('Analytic Posterior Mean:'); +[Ep,Cp]=mci_linear_post(M,U,Y); +disp(Ep) + +dist{1}=post; +dist{1}.color='k'; +dist{1}.names=U.names; + +dist{2}.Ep=Ep; +dist{2}.Cp=Cp; +dist{2}.names=dist{1}.names; +dist{2}.type='gaussian'; +dist{2}.color='r'; +figure +Np=length(M.pE); +for j=1:Np, + subplot(1,Np,j); + mci_plot_dist(dist{1},j); + hold on + mci_plot_dist(dist{2},j); + grid on +end + +ind=[1:size(post.Ce,3)]; +mci_plot_noiseSD (post.Ce,ind); + +% Plot model fits +names={'True','MCI','Analytic'}; +y{1}=Xfull*w_true; +y{2}=Xfull*post.Ep; +y{3}=Xfull*Ep; +figure +mci_linear_plot_fit (M,Y,y,names); + +spm_mci_diag(post); + +stats = spm_mci_mvnpost (post,'ESS') +stats = spm_mci_mvnpost (post,'thinning') + +[pstat,mu,nse,batch]=spm_mci_stat(post) + +figure +plot(sqrt(squeeze(post.Ce(1,1,:)))); +grid on +title('Noise SD'); \ No newline at end of file diff --git a/toolbox/mci/demo-models/mci_demo_logistic.m b/toolbox/mci/demo-models/mci_demo_logistic.m new file mode 100644 index 00000000..a48b3e86 --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_logistic.m @@ -0,0 +1,47 @@ + +clear all +close all + +disp('Logistic regression model'); + +%[M,U,Y] = mci_logistic_struct ('ripley'); +%[M,U,Y] = mci_logistic_struct ('pima'); + +[M,U] = mci_logistic_struct ('dct'); +w_true=[0.2,8,6]'; +[tmp,Y.y] = mci_logistic_gen(w_true,M,U); + +%mcmc.inference='amc'; +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.verbose=0; +mcmc.maxits=1024; + +post = spm_mci_post (mcmc,M,U,Y); + +% q=post.quantiles; +% Np=size(q,1); +% figure +% lw=2; +% errorbar([1:Np],q(:,2),q(:,1),q(:,3),'k','LineWidth',lw); +% set(gca,'FontSize',16); +% grid on +% xlabel('Parameter'); +% title('Posterior median and 95% intervals'); + +if ~strcmp(mcmc.inference,'vl') + diag.traceplot=1; + diag.eplot=1; + if strcmp(mcmc.inference,'langevin') + diag.bplot=1; + else + diag.bplot=0; + end + spm_mci_diag(post,diag); + + stats = spm_mci_mvnpost (post,'ESS') + stats = spm_mci_mvnpost (post,'thinning') + + spm_mci_quantiles (post,2); +end diff --git a/toolbox/mci/demo-models/mci_demo_nmm.m b/toolbox/mci/demo-models/mci_demo_nmm.m new file mode 100644 index 00000000..23f963a3 --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_nmm.m @@ -0,0 +1,105 @@ + +clear all +close all + +% Number of parameters to estimate +Np=2; +%Np=6; +%Np=21; + +disp('Synthetic data from two region Neural Mass Model'); +disp(sprintf('Model has %d parameters',Np)); + +% Backward connection ? +back=1; + +% Observation noise SD +sd=0.01; + +[M,U] = mci_nmm_struct (back,sd,Np); + +%M.Ce=diag([0.03^2,0.01^2]); + +% Parameters +switch Np + case 2 + disp('Estimating extrinsic connections'); + P=[1,1]'; + %P=[0.5,1]'; + case 6 + disp('Estimating intrinsic and extrinsic connections'); + P=[1,1,0,0,1,0]'; + case 21, + disp('Estimating all connections'); + % All params set to prior mean except f/b + P=M.pE; + P.A{1}(2,1)=1; % Forward connection + P.A{2}(1,2)=1; % Backward connection +end + +Y = mci_nmm_gen (M,U,P); + +mci_plot_outputs(M,Y); + +%mcmc.inference='amc'; +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.maxits=1024; +mcmc.verbose=0; + +post = spm_mci_post (mcmc,M,U,Y,P); + +switch mcmc.inference + case 'langevin', + ind=[1:size(post.Ce,3)]; + mci_plot_noiseSD (post.Ce,ind); + case 'vl', + disp('Error SD:'); + disp(sqrt(diag(post.Ce))); +end + +diag.traceplot=1; +diag.eplot=1; +if strcmp(mcmc.inference,'langevin') + diag.bplot=1; +else + diag.bplot=0; +end +spm_mci_diag(post,diag); + +disp(sprintf('Elapsed time:%1.2f',post.els)); + +% Plot posterior surface +plot_post=0; +if plot_post + S.Nbins=20; + S.pxy(1,:)=linspace(-0.2,1.5,S.Nbins); + S.pxy(2,:)=linspace(-0.2,1.5,S.Nbins); + S.param{1}='P(1)'; + S.param{2}='P(2)'; + S.name={'P(1)','P(2)'}; + %[L,S] = mci_plot_surface (P,M,U,Y,S,'prior'); + %[L,S] = mci_plot_surface (P,M,U,Y,S,'like'); + [L,S] = mci_plot_surface (P,M,U,Y,S,'post'); + hold on + ms=10; + plot(M.pE(1),M.pE(2),'wo','MarkerSize',ms); + plot(post.Ep(1),post.Ep(2),'wx','MarkerSize',ms); + plot(P(1),P(2),'w+','MarkerSize',ms); + j=post.ind; + plot(post.P(1,j),post.P(2,j),'w.'); +end + +stats = spm_mci_mvnpost (post,'ESS') +stats = spm_mci_mvnpost (post,'thinning') + +spm_mci_quantiles (post,1,0); +xlabel('\theta_1'); +ylabel('p(\theta_1|Y)'); + +spm_mci_quantiles (post,2,0); +xlabel('\theta_2'); +ylabel('p(\theta_2|Y)'); + +%[x,pnum,pgauss]=spm_mci_postslices (post,M,U,Y); diff --git a/toolbox/mci/demo-models/mci_demo_phase.m b/toolbox/mci/demo-models/mci_demo_phase.m new file mode 100644 index 00000000..d9d19bad --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_phase.m @@ -0,0 +1,30 @@ + +clear all +close all + +disp('Synthetic data from d-region weakly coupled oscillator'); +disp('Fully connected network'); + +M.N=200; +M.T=1; +dt=M.T/M.N; +M.t=[1:M.N]'*dt; +M.f='mci_phase_fx'; +M.g='mci_phase_gx'; + +% Dimension of state space +d=3; +[P,M,U,Y] = mci_phase_init(d); + +mci_plot_outputs(M,cos(2*pi*Y)); + +% Check to see if VL will run +%tmp=spm_int(M.pE,M,U); + +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.verbose=1; +mcmc.maxits=256; + +post = spm_mci_post (mcmc,M,U,Y,P); \ No newline at end of file diff --git a/toolbox/mci/demo-models/mci_demo_rphase.m b/toolbox/mci/demo-models/mci_demo_rphase.m new file mode 100644 index 00000000..399d4ed4 --- /dev/null +++ b/toolbox/mci/demo-models/mci_demo_rphase.m @@ -0,0 +1,53 @@ + +clear all +close all + +disp('Synthetic data from d-region weakly coupled oscillator'); +disp('Model with reduced (not full) connectivity'); + +% Dimension of state space +d=3; + +conn(1).regions=[1,2]; +conn(1).name='vis2mtl'; +conn(2).regions=[3,2]; +conn(2).name='vis2ifg'; + +[M,U] = mci_rphase_struct(d,conn); + +M.freq=6; +M.x0=[-pi/2,0,pi/2]'; + +rand_init=0; +if rand_init + P=spm_normrnd(M.vpE,M.pC,1); +else + %pars.aconn=[2.75,-2.65]'; + %pars.bconn=[2.59,1.85]'; + pars.aconn=[-0.6 -0.6]; + pars.bconn=[0,0]'; + P=spm_vec(pars); +end + +Y = mci_rphase_gen(P,M,U); + +mci_plot_outputs(M,cos(Y)); + +%mcmc.inference='vl'; +mcmc.inference='langevin'; + +mcmc.maxits=1024; +mcmc.verbose=0; + +post = spm_mci_post (mcmc,M,U,Y,P); + +spm_mci_diag(post); + +stats = spm_mci_mvnpost (post,'ESS') +stats = spm_mci_mvnpost (post,'thinning') +stats = spm_mci_mvnpost (post,'MAR') + + +for j=1:length(P), + spm_mci_quantiles (post,j,0); +end \ No newline at end of file diff --git a/toolbox/mci/demo-thermodynamic/mci_demo_linsqr.m b/toolbox/mci/demo-thermodynamic/mci_demo_linsqr.m new file mode 100644 index 00000000..96a143ad --- /dev/null +++ b/toolbox/mci/demo-thermodynamic/mci_demo_linsqr.m @@ -0,0 +1,100 @@ + +clear all +close all + +disp('Linear regression with squared parameters'); + +% Design type +%des='linear-offset'; +%des='quadratic-offset'; +des='dct'; +%des='quadratic'; + +% Parameters for first level +Nobs=100; +sd=0.1; +lambda=1/(sd^2); + +[M,U,Xfull] = mci_linsqr_struct (Nobs,lambda,des); + +% Generate Data +%w_true = spm_normrnd(M.pE,M.pC,1); +w_true = [2, 2]'; +Y_true = mci_linsqr_gen (w_true,M,U); +Y = Y_true + sqrt(M.Ce)*randn(Nobs,1); + +% figure; +% lw=2; +% plot(M.t,Y_true,'k','LineWidth',lw); +% hold on +% plot(M.t,Y,'.'); +% return + +%mcmc.inference='amc'; +%mcmc.inference='vl'; +%mcmc.inference='langevin'; + +%mcmc.inference='multi-amc'; +%mcmc.J=512; +%mcmc.maxits=128; + +mcmc.inference='ais'; +mcmc.anneal='geometric'; +mcmc.prop='lmc'; +mcmc.nprop=1; +mcmc.J=512; +mcmc.maxits=32; + +mcmc.verbose=0; + +post = spm_mci_post (mcmc,M,U,Y,w_true); +disp('Posterior Mean from MCI'); +disp(post.Ep) +Y_post = mci_linsqr_gen (post.Ep,M,U); + +if strcmp(mcmc.inference,'ais') + figure;plot(post.beta,mean(post.acc)); + title('Mean acceptance rate over trajectories'); + xlabel('Inverse temperature, \beta'); + ylabel(''); + figure;plot(post.q);title('Normalised importance weights'); + xlabel('Sample,i');ylabel('q_i'); +end + +if ~strcmp(mcmc.inference,'vl') + figure;plot(post.P(1,:),post.P(2,:),'.'); title('Posterior samples'); + xlabel('P(1)');ylabel('P(2)'); +end + +figure; +lw=2; +plot(M.t,Y_true,'k','LineWidth',lw); +hold on +plot(M.t,Y,'.'); +plot(M.t,Y_post,'r','LineWidth',lw); + +plot_post=1; +if plot_post + % Plot posterior surface + S.Nbins=50; + r=2; + S.pxy(1,:)=linspace(-r*w_true(1),r*w_true(1),S.Nbins); + S.pxy(2,:)=linspace(-r*w_true(2),r*w_true(2),S.Nbins); + S.param{1}='P(1)'; + S.param{2}='P(2)'; + S.name={'w_1','w_2'}; + [L,S] = mci_plot_surface (w_true,M,U,Y,S,'post'); + hold on + ms=10; + plot(M.pE(1),M.pE(2),'wo','MarkerSize',ms); + plot(post.Ep(1),post.Ep(2),'wx','MarkerSize',ms); + if ~strcmp(mcmc.inference,'vl') + plot(post.P(1,:),post.P(2,:),'w.'); + end + %plot(w_true(1),w_true(2),'k+','MarkerSize',ms); +end + +spm_mci_diag(post); + +stats = spm_mci_mvnpost (post,'ESS') +stats = spm_mci_mvnpost (post,'thinning') diff --git a/toolbox/mci/demo-thermodynamic/mci_demo_ramsay.m b/toolbox/mci/demo-thermodynamic/mci_demo_ramsay.m new file mode 100644 index 00000000..05368b93 --- /dev/null +++ b/toolbox/mci/demo-thermodynamic/mci_demo_ramsay.m @@ -0,0 +1,95 @@ + +clear all +close all + +disp('Synthetic data from nonlinear ODE model'); +disp('defined in Ramsay et al. (2007)'); +disp('based on Van der Pol oscillator and which'); +disp('reduces to Fitzhugh-Nagumo for certain parameters'); + +sigma_e=0.01; +[M,U] = mci_ramsay_struct(sigma_e); + +% Use higher integration tolerances than by default +M.reltol=1e-3; +M.abstol=1e-5; + +rand_init=0; +if rand_init + P=spm_normrnd(M.vpE,M.pC,1); +else + P=[log(0.2) log(0.2)]'; +end + +tic; +Y = mci_ramsay_gen(P,M,U); +toc + +mci_plot_outputs(M,Y); + +%mcmc.inference='vl'; +%mcmc.inference='langevin'; +%mcmc.maxits=16; +%mcmc.verbose=1; + +mcmc.inference='ais'; +mcmc.anneal='geometric'; +mcmc.prop='lmc'; +mcmc.nprop=1; +mcmc.J=16; +mcmc.maxits=8; +mcmc.rec_traj=1; + +post = spm_mci_post (mcmc,M,U,Y,P); + +if ~strcmp(mcmc.inference,'vl') + spm_mci_diag(post); + + stats = spm_mci_mvnpost (post,'ESS') + stats = spm_mci_mvnpost (post,'thinning') + + for j=1:length(P), + spm_mci_quantiles (post,j,0); + end +end + +load ramsay-surface +figure +surf(S.x,S.y,L); +xlabel(S.name{1}); +ylabel(S.name{2}); + +figure; +imagesc(S.pxy(1,:),S.pxy(2,:),L); +axis xy +hold on +xlabel(S.name{1}); +ylabel(S.name{2}); +hold on +ms=10; +plot(M.pE(1),M.pE(2),'wo','MarkerSize',ms); +plot(P(1),P(2),'w+','MarkerSize',ms); +if ~strcmp(mcmc.inference,'vl') + j=post.ind; + plot(post.P(1,j),post.P(2,j),'w.'); +end +plot(post.Ep(1),post.Ep(2),'kx','MarkerSize',ms); + +if strcmp(mcmc.inference,'ais') + % plot individual trajectories + figure + xlim([-2 0]); + ylim([-2 0]); + hold on + for i=1:mcmc.maxits, + xx=squeeze(post.traj(i,1,:)); + yy=squeeze(post.traj(i,2,:)); + plot(xx,yy,'k-'); + plot(xx(end),yy(end),'kx','MarkerSize',ms); + xlabel(S.name{1}); + ylabel(S.name{2}); + disp('Press space to see more trajectories'); + pause + + end +end diff --git a/toolbox/mci/demo-thermodynamic/mci_demo_ramsay_surface.m b/toolbox/mci/demo-thermodynamic/mci_demo_ramsay_surface.m new file mode 100644 index 00000000..66e827ee --- /dev/null +++ b/toolbox/mci/demo-thermodynamic/mci_demo_ramsay_surface.m @@ -0,0 +1,42 @@ + +clear all +close all + +disp('Synthetic data from nonlinear ODE model'); +disp('defined in Ramsay et al. (2007)'); +disp('based on Van der Pol oscillator and which'); +disp('reduces to Fitzhugh-Nagumo for certain parameters'); + +disp(' '); +disp('Create data for posterior surface plot'); + +sigma_e=0.01; +[M,U] = mci_ramsay_struct(sigma_e); + +% Use higher integration tolerances than by default +M.reltol=1e-3; +M.abstol=1e-5; + +rand_init=0; +if rand_init + P=spm_normrnd(M.vpE,M.pC,1); +else + P=[log(0.2) log(0.2)]'; +end + +Y = mci_ramsay_gen(P,M,U); +mci_plot_outputs(M,Y); + +S.Nbins=50; +S.pxy(1,:)=linspace(-2,0,S.Nbins); +S.pxy(2,:)=linspace(-2,0,S.Nbins); +S.param{1}='P(1)'; +S.param{2}='P(2)'; +S.name={'log a','log b'}; +%[L,S] = mci_plot_surface (P,M,U,Y,S,'prior'); +%[L,S] = mci_plot_surface (P,M,U,Y,S,'like'); +tic; +[L,S] = mci_plot_surface (P,M,U,Y,S,'post'); +toc + +save ramsay-surface L S \ No newline at end of file diff --git a/toolbox/mci/demo-thermodynamic/mci_demo_ti.m b/toolbox/mci/demo-thermodynamic/mci_demo_ti.m new file mode 100644 index 00000000..5d52110c --- /dev/null +++ b/toolbox/mci/demo-thermodynamic/mci_demo_ti.m @@ -0,0 +1,74 @@ + +clear all +close all + +disp('Thermodynamic Integration Example'); + +%model_type='linear'; +%model_type='growth'; +model_type='logistic-dct'; + +switch model_type + + case 'linear', + Nobs=20; + sd=0.2; + lambda=1/(sd^2); + [M{1},U{1}] = mci_linear_struct (Nobs,lambda,'dct'); + w_true = spm_normrnd(M{1}.pE,M{1}.pC,1); + Y = U{1}.X*w_true+sqrt(M{1}.Ce)*randn(Nobs,1); + + case 'growth', + Nobs=40; + [M{1},U{1}] = mci_pb_struct (Nobs); + M{1}.Ce=4; + Ptrue = spm_normrnd(M{1}.pE,M{1}.pC,1); + yhat = mci_pb_gen (Ptrue,M{1},U{1}); + Y = yhat + sqrt(M{1}.Ce)*randn(Nobs,1); + + case 'logistic-ripley', + [M{1},U{1},Y] = mci_logistic_struct ('ripley'); + + case 'logistic-pima'; + [M{1},U{1},Y] = mci_logistic_struct ('pima'); + + case 'logistic-dct'; + [M{1},U{1},Y] = mci_logistic_struct ('dct'); + Ptrue=[1 10 10]'; + [g,Y]=mci_logistic_gen(Ptrue,M{1},U{1}); +end + +% Set MCMC parameters +mcmc.nscale=250; +mcmc.ntune=250; +mcmc.nsamp=512; +mcmc.J=64; % Number of temperatures/chains + +mcmc.remove_burn_in=1; + +% No sharing of samples between chains +mcmc.gprob=0; + +tic; +[P,logev,D] = spm_mci_pop (mcmc,M,U,Y); +toc + +disp(sprintf('TI Log Evidence = %1.2f',logev.ti)); + +% Annealed Importance Sampling +mcmc.inference='ais'; +mcmc.anneal='geometric'; +mcmc.prop='lmc'; +mcmc.nprop=1; +mcmc.J=512; +mcmc.maxits=64; + +tic; +post = spm_mci_ais (mcmc,M{1},U{1},Y); +toc +disp(sprintf('AIS Log Evidence = %1.2f',post.logev)); + +if strcmp(model_type,'linear') + [Ep,Cp,L] = mci_linear_post (M{1},U{1},Y); + disp(sprintf('Analytic log evidence = %1.2f',L)); +end \ No newline at end of file diff --git a/toolbox/mci/demo-thermodynamic/mci_demo_ti_chains.m b/toolbox/mci/demo-thermodynamic/mci_demo_ti_chains.m new file mode 100644 index 00000000..5d67a3b8 --- /dev/null +++ b/toolbox/mci/demo-thermodynamic/mci_demo_ti_chains.m @@ -0,0 +1,81 @@ + +clear all +close all + +disp('Compute Model Evidence for Logistic Regression models using'); +disp('Thermodynamic Integration'); +disp('Explore effect of number of chains'); + +% Logistic model +[M{1},U{1}] = mci_logistic_struct ('dct'); +P=[1 10 10]'; +[g,Y]=mci_logistic_gen(P,M{1},U{1}); +figure; +plot(g,'k','LineWidth',2); +set(gca,'FontSize',16); +grid on +xlabel('Time,t'); +ylabel('p(Y=1|t)'); + +% MCMC parameters +mcmc.J=64; % Number of temperatures/chains +mcmc.ntune=500; +mcmc.nsamp=250; +setinit=1; +if setinit + % Start sampling from this point + for j=1:mcmc.J, + mcmc.init{j}=P; + end + mcmc.nscale=0; +else + mcmc.nscale=500; +end +mcmc.remove_burn_in=1; + +% No sharing of samples between chains +mcmc.gprob=0; + +j=[4,8,16,32,64]; +for i=1:length(j), + mcmc.J=j(i); + tic; + [P,logev,D] = spm_mci_pop (mcmc,M,U,Y); + els(i)=toc; + L(i)=logev.ti; + disp(sprintf('J=%d: TI Log Evidence = %1.2f',j(i),logev.ti)); +end + +figure +plot(j,L,'k','LineWidth',2); +grid on +set(gca,'FontSize',16); +xlabel('Number of Chains'); +ylabel('Log Evidence'); +title('Thermodynamic Integration'); + +% Annealed Importance Sampling +mcmc.inference='ais'; +mcmc.anneal='geometric'; +mcmc.prop='lmc'; +mcmc.nprop=1; +mcmc.maxits=64; + + +j=[4,8,16,32,64,128,256,512,1024]; +for i=1:length(j), + mcmc.J=j(i); + tic; + post = spm_mci_ais (mcmc,M{1},U{1},Y); + els(i)=toc; + L(i)=post.logev; + disp(sprintf('J=%d: AIS Log Evidence = %1.2f',j(i),L(i))); +end + +figure +semilogx(j,L,'k','LineWidth',2); +grid on +set(gca,'FontSize',16); +xlabel('Number of Chains'); +ylabel('Log Evidence'); +title('Annealed Importance Sampling'); diff --git a/toolbox/mci/demo-thermodynamic/mci_demo_ti_compare.m b/toolbox/mci/demo-thermodynamic/mci_demo_ti_compare.m new file mode 100644 index 00000000..82ce9e07 --- /dev/null +++ b/toolbox/mci/demo-thermodynamic/mci_demo_ti_compare.m @@ -0,0 +1,130 @@ + +clear all +close all + +disp('Model comparison using'); +disp('Thermodynamic Sampling'); + +model_type='linear'; +%model_type='logistic'; + +annealing=0; +model_switch=0; +ais=1; + +switch model_type + + case 'linear', + disp('Linear Regression'); + Nobs=20; + sd=0.2; + lambda=1/(sd^2); + [M{1},U{1}] = mci_linear_struct (Nobs,lambda,'dct'); + w_true = spm_normrnd(M{1}.pE,M{1}.pC,1); + Y = U{1}.X*w_true+sqrt(M{1}.Ce)*randn(Nobs,1); + + % Alternative model has fewer variables + Nred=6; % Number of parameters in reduced model + AM{1}=M{1}; + AU{1}.X=U{1}.X(:,1:Nred); + AM{1}.pE=zeros(Nred,1); + AM{1}.pC=M{1}.pC(1:Nred,1:Nred); + + % Indices of parameters to remove + reduced_ind=[Nred+1:7]; + + case 'logistic', + disp('Logistic Regression'); + [M{1},U{1}] = mci_logistic_struct ('dct'); + P=[1 10 10]'; + [g,Y]=mci_logistic_gen(P,M{1},U{1}); + + % Alternative model has no third variable + reduced_ind=3; + AM{1}=M{1}; + AU{1}.X=U{1}.X(:,1:2); + AM{1}.pE=zeros(2,1); + AM{1}.pC=100*eye(2); + +end + +% Set MCMC parameters +mcmc.nscale=500; +mcmc.ntune=500; +mcmc.nsamp=1000; +mcmc.J=16; % Number of temperatures/chains + +mcmc.remove_burn_in=1; + +% No sharing of samples between chains +mcmc.gprob=0; + +if annealing + disp('TI Annealing ...'); + tic; + [P,logev1,D] = spm_mci_pop (mcmc,M,U,Y); + toc + tic; + [P,logev2,D] = spm_mci_pop (mcmc,AM,AU,Y); + toc + disp(sprintf('TI Annealing Log Evidence Model 1 = %1.2f',logev1.ti)); + disp(sprintf('TI Annealing Log Evidence Model 2= %1.2f',logev2.ti)); +end + +if strcmp(model_type,'linear') + [Ep,Cp,L1] = mci_linear_post (M{1},U{1},Y); + [Ep,Cp,L2] = mci_linear_post (AM{1},AU{1},Y); + disp(sprintf('Analytic log evidence Model 1 = %1.2f',L1)); + disp(sprintf('Analytic log evidence Model 2 = %1.2f',L2)); +end + +if model_switch + % TI Model Switch + M{2}=M{1}; + U{2}=U{1}; + for i=1:length(reduced_ind), + k=reduced_ind(i); + M{2}.pC(k,k)=1e-4; + end + mcmc.anneal='sigmoid'; + + disp('TI Model Switching ...'); + tic; + [Psd,logswitch,D] = spm_mci_pop (mcmc,M,U,Y); + toc +end + +if ais + disp('Annealed Importance Sampling ...'); + mcmc.inference='ais'; + mcmc.anneal='geometric'; + mcmc.prop='lmc'; + mcmc.nprop=1; + mcmc.J=512; + mcmc.maxits=32; + + tic; + post = spm_mci_ais (mcmc,M{1},U{1},Y); + ais_logev1=post.logev; + toc + tic; + post = spm_mci_ais (mcmc,AM{1},AU{1},Y); + ais_logev2=post.logev; + toc + disp(sprintf('AIS Log Evidence Model 1 = %1.2f',ais_logev1)); + disp(sprintf('AIS Log Evidence Model 2= %1.2f',ais_logev2)); +end + +disp(' '); +if annealing + disp(sprintf('TI Annealing Log Bayes Factor = %1.2f',logev2.ti-logev1.ti)); +end +if strcmp(model_type,'linear') + disp(sprintf('Analytic Log Bayes factor = %1.2f',L2-L1)); +end +if model_switch + disp(sprintf('TI Switching Log Bayes Factor = %1.2f',logswitch.ti)); +end +if ais + disp(sprintf('AIS Log Bayes Factor = %1.2f',ais_logev2-ais_logev1)); +end \ No newline at end of file diff --git a/toolbox/mci/demo-thermodynamic/ramsay-surface.mat b/toolbox/mci/demo-thermodynamic/ramsay-surface.mat new file mode 100644 index 00000000..32ca395c Binary files /dev/null and b/toolbox/mci/demo-thermodynamic/ramsay-surface.mat differ diff --git a/toolbox/mci/gradients/mci_compare_setup.m b/toolbox/mci/gradients/mci_compare_setup.m new file mode 100644 index 00000000..e31c1a44 --- /dev/null +++ b/toolbox/mci/gradients/mci_compare_setup.m @@ -0,0 +1,34 @@ +function [P,M,U,Y,ind] = mci_compare_setup (model) +% Set up data structures for fwd/sens/grad comparisons +% FORMAT [P,M,U,Y,ind] = mci_compare_setup (model) +% +% model 'phase', 'nmm-r2p2' +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_compare_setup.m 6548 2015-09-11 12:39:47Z will $ + +switch model, + + case 'phase', + d=7; + disp(sprintf('Weakly coupled oscillator network : d=%d regions',d)); + [P,M,U,Y] = mci_phase_init(d); + ind=[1:M.Np-M.n-1]; + + case 'nmm-r2p2', + disp('Two-region, two-parameter neural mass model'); + back=1; + sd=0.01; + Np=2; + + [M,U] = mci_nmm_struct(back,sd,Np); + P=[1,1]'; + Y = mci_nmm_gen(M,U,P); + ind=[1:M.Np]; + + otherwise + disp('Unknown model'); + return +end diff --git a/toolbox/mci/gradients/spm_mci_adjoint.m b/toolbox/mci/gradients/spm_mci_adjoint.m new file mode 100644 index 00000000..ed94edc9 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_adjoint.m @@ -0,0 +1,66 @@ +function [dLdp,g,x] = spm_mci_adjoint (Pr,M,U,Y) +% Gradient of log joint from adjoint method +% FORMAT [dLdp,g,x] = spm_mci_adjoint (Pr,M,U,Y) +% +% Pr Parameters (vectorised and in M.V subspace) +% M Model structure +% U Inputs [Nin x N] +% Y Data +% +% dLdp Gradient [Np x 1] +% g Outputs [N x Nout] +% x States [N x Nstates] +% +% If M.adjlike=1 this function returns gradient of log likelihood +% +% This function uses integrators from Matlab's ODE Suite +% +% B. Sengupta, K. Friston and W. Penny (2014) Efficient Gradient +% Computation for Dynamical Models. Neuroimage,98, 521-527. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_adjoint.m 6548 2015-09-11 12:39:47Z will $ + +try adjlike=M.adjlike; catch adjlike=0; end + +% Parameters in original space +P = M.V*Pr+M.vpE; + +if isempty(U) + U=zeros(1,M.N); +end + +[g,x] = spm_mci_fwd(P,M,U); + +% Gradient at computed times +% When computing output sensitivities, assume dydx=L, ie not a +% function of x. Generalise later +[tmp,L]=feval(M.g,M.x0,U(:,1),P,M); +e=Y-g; +djdx=e*M.iCe*L; + +% integrate adjoint equation +lambda = spm_mci_adjoint_int(U,P,M,x,djdx); + +% If observation function becomes dependent on parameters +% djdp will need updating +djdp = zeros(1,M.Np); + +dLdp=zeros(1,M.Np); +for n=1:M.N, + % Evaluate parameter Jacobian + if isfield(M,'dfdp') + Fp = feval(M.dfdp,x(n,:)',U(:,n),P,M); + else + Fp = spm_diff(M.f,x(n,:)',U(:,n),P,M,3); + end + dLdp=dLdp+djdp-lambda(n,:)*Fp; +end + +if ~adjlike + dlogpriordp = spm_mci_gprior_deriv (Pr,M); + dLdp=dLdp+dlogpriordp; +end + diff --git a/toolbox/mci/gradients/spm_mci_adjoint_int.m b/toolbox/mci/gradients/spm_mci_adjoint_int.m new file mode 100644 index 00000000..90a35291 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_adjoint_int.m @@ -0,0 +1,69 @@ +function [lambda] = spm_mci_adjoint_int (U,P,M,V,djdx,tol) +% Integrate adjoint equation +% FORMAT [lambda] = spm_mci_adjoint_int (U,P,M,V,djdx,tol) +% +% U Inputs +% P Parameters +% M Model structure +% V states +% djdx derivative of log likelihood wrt states +% tol tolerances +% +% lambda adjoint parameters, at times M.t +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_adjoint_int.m 6548 2015-09-11 12:39:47Z will $ + +init_t=M.t(1); +final_t=M.T; + +tol.abs=1e-3; +tol.rel=1e-5; + +% parameters for the intergrator +options = odeset('AbsTol',tol.abs,'RelTol',tol.rel); + +% integrate adjoint equation backwards +lam_init=zeros(M.n,1); +[TT, ld] = ode15s(@(t,lam) integrate_adjoints(t,lam,djdx,V,U,P,M),[final_t init_t], lam_init, options); + +% interpolate to M.t +% (Note, interp1q is not accurate enough here +% as TT typically has less resolution than M.t) +lambda = interp1(TT,ld,M.t,'spline'); + +end + +%-------------------------------------------------------------------------- +function DlDt = integrate_adjoints(t,lam,djdx,V,U,P,M) + +Nx=M.n; +DlDt = zeros(Nx,1); + +% Interpolate state and dj/dx to current time point +for d=1:Nx, + local_djdx(d)=interp1q(M.t,djdx(:,d),t); + v(d)=interp1q(M.t,V(:,d),t); +end + +% Find nearest time point for which we have pre-computed input +if isempty(U) + ut=[]; +else + [tmp,ind]=min(abs(t-M.t)); + ut=U(:,ind); +end + +% Evaluate state Jacobian +if isfield(M,'dfdx') + Fx = feval(M.dfdx,v(:),ut,P,M); +else + Fx = spm_diff(M.f,v(:),ut,P,M,1); +end + +DlDt=local_djdx-lam'*Fx; +DlDt=DlDt'; + +end \ No newline at end of file diff --git a/toolbox/mci/gradients/spm_mci_adjoint_sun.m b/toolbox/mci/gradients/spm_mci_adjoint_sun.m new file mode 100644 index 00000000..8959c798 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_adjoint_sun.m @@ -0,0 +1,262 @@ +function [dLdp] = spm_mci_adjoint_sun (Pr,M,U,Y) +% Gradient of log joint from adjoint method (via Sundials) +% FORMAT [dLdp] = spm_mci_adjoint_sun (Pr,M,U,Y) +% +% Pr Parameters (vectorised and in M.V subspace) +% M Model structure +% U Inputs [Nin x N] +% Y Data +% +% dLdp Gradient [Np x 1] +% +% For M.adjlike=1, dLdp is gradient of log likelihood not log joint +% (useful for debugging). +% +% For M.backint=1 (default), compute the integral underlying dLdp +% *during* backwards integration of adjoint. For M.backint=0, this +% integral is computed *after* adjoint (useful for debugging). +% +% B. Sengupta, K. Friston and W. Penny (2014) Efficient Gradient +% Computation for Dynamical Models. Neuroimage,98, 521-527. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_adjoint_sun.m 6548 2015-09-11 12:39:47Z will $ + +try backint=M.backint; catch backint=1; end +try adjlike=M.adjlike; catch adjlike=0; end + +% Tolerances +tol_scale = 1e-3; +reltol = 1e-3; +abstol = 1e-4; + +% Parameters in original space +P = M.V*Pr+M.vpE; + +%tDur=[0 M.t(end)]; +%tDur=[M.t(1) M.t(end)]; + +t0=0; +%tf=M.T; +%dt=M.t(2)-M.t(1); +%t0=M.t(1)-dt; +tf=M.T; + +if isempty(U) + U=zeros(1,M.N); +end + +data.U=U; +data.P=P; +data.M=M; +data.Y=Y; + +options = CVodeSetOptions('UserData', data,... + 'RelTol',reltol, ... + 'AbsTol',abstol, ... + 'LinearSolver','Dense', ... + 'JacobianFn',@djacfn); + +CVodeInit(@spm_mci_flow_sun, 'BDF', 'Newton', t0, M.x0, options); + +CVodeAdjInit(150, 'Hermite'); + +optionsB = CVodeSetOptions('UserData',data,... + 'MaxNumSteps',50000, ... + 'RelTol',reltol,... + 'AbsTol',abstol,... + 'LinearSolver','Dense',... + 'JacobianFn',@djacBfn); + +lambda_init=zeros(M.n,1); +iB = CVodeInitB(@rhsadjoint, 'BDF', ... + 'Newton', tf, lambda_init, optionsB); + +if backint + % Use CVODE to compute the integral underlying dLdp + % *during* backwards integration of adjoint (backint=1) + + [status,t,y] = CVode(tf,'Normal'); + + optionsQB = CVodeQuadSetOptions('ErrControl',true,... + 'RelTol',reltol,... + 'AbsTol',abstol); + + qB1 = zeros(M.Np,1); + CVodeQuadInitB(iB, @paramgrad, qB1, optionsQB); + + % Backward integration of the adjoint equation + % (yB is the adjoint vector) + [status,t,yB,qB] = CVodeB(M.t(2),'Normal'); + %[status,t,yB,qB] = CVodeB(t0,'Normal'); + if status == -1 + error('Adjoint integration failed'); + end + qB = -qB'; + dt = M.t(2)-M.t(1); + dLdp = qB/dt; +else + % Compute integral underlying dLdp *after* backwards + % integration of adjoint + + ntout = 1000; + dt = (tf-t0)/ntout; + tt = linspace(t0+dt,tf,ntout-1); + + %[status,ttf,x] = CVode(M.t(2:end),'Normal'); + %[status,ttf,x] = CVode(tt,'Normal'); + %[status,ttf,x] = CVode(M.t(2:end),'Normal'); + [status,ttf,x] = CVode(M.t,'Normal'); + interp_x = (interp1q(ttf',x',M.t))'; + + %tm(1) = tDur(2); + %t = tDur(2); + tm(1) = tf; + t = tf; + it = 1; + % Integrate adjoint equation + while t > M.t(2) + %while t > t0+2*dt + it = it+1; + % The adjoint vector is yB + %[status,t,yB] = CVodeB(tDur(1),'OneStep'); + [status,t,yB] = CVodeB(M.t(1),'OneStep'); + %[status,t,yB] = CVodeB(t0+2*dt,'OneStep'); + if status == -1 + error('Adjoint integration failed'); + end + tm(it) = t; + dldp(it,:) = feval(@paramgrad,t, interp_x, yB, data); + end + dldp_int=interp1(tm,dldp,M.t,'spline'); + dLdp=sum(dldp_int); +end +CVodeFree; + +if ~adjlike + dlogpriordp = spm_mci_gprior_deriv (Pr,M); + dLdp=dLdp+dlogpriordp; +end + +end + +% ----------------------------------------------------------- +function [qBd, flag, new_data] = paramgrad(t, x, yB, data) +% Np quadratures for parametric gradient +% t time +% x state +% yB adjoint vector, lambda^T +% data contains P,M,U,Y +% +% yBd dlambda^T/dt + +P = data.P; +M = data.M; +U = data.U; + +% If observation function becomes dependent on parameters +% we'll need to update term1 +term1 = 0; + +% Find nearest time point for which we have pre-computed input +if isempty(U) + ut=[]; +else + [tmp,ind]=min(abs(t-M.t)); + ut=U(:,ind); +end + +% Evaluate parameter Jacobian +if isfield(M,'dfdp') + dfdp = feval(M.dfdp,x,ut,P,M); +else + dfdp = spm_diff(M.f,x,ut,P,M,3); +end +term2 = -yB'*dfdp; +qBd = term1 + term2; + +flag = 0; +new_data = []; + +end + +% ----------------------------------------------------------- +function [yBd, flag, new_data] = rhsadjoint(t, x, yB, data) +% Adjoint equation +% FORMAT [yBd, flag, new_data] = rhsadjoint(t, x, yB, data) +% +% t time +% x state +% yB adjoint vector, lambda^T +% data contains P,M,U,Y +% +% yBd dlambda^T/dt + +Y = data.Y; +M = data.M; +U = data.U; +P = data.P; + +% Interpolate data to required time point +for j=1:M.l, + ydata(j)=interp1q(M.t,Y(:,j),t); +end +[y,L]=feval(M.g,x,U(:,1),P,M); +e=ydata-y'; + +term1 = (djacfn(t,x,[],data))'*yB; % (f_y)^T \lambda + +% When computing output sensitivities, assume dydx=L, ie not a +% function of x. Generalise later +dydx=L; + +term2 = e*M.iCe*dydx; +yBd = (-term1 + term2')'; + +flag = 0; +new_data = []; + +end + +% ----------------------------------------------------------- +function [JB, flag, new_data] = djacBfn(t, y, yB, fyB, data) +% Backward problem Jacobian function + +J = djacfn(t,y,[],data); +JB = -J'; + +flag = 0; +new_data = []; + +end + +% ----------------------------------------------------------- +function [J, flag, new_data] = djacfn(t, y, fy, data) +% State Jacobian at time t + +P=data.P; +M=data.M; +U=data.U; + +% Find nearest time point for which we have pre-computed input +if isempty(U) + ut=[]; +else + [tmp,ind]=min(abs(t-M.t)); + ut=U(:,ind); +end + +% Evaluate state Jacobian +if isfield(M,'dfdx') + J = feval(M.dfdx,y,ut,P,M); +else + J = spm_diff(M.f,y,ut,P,M,1); +end + +flag = 0; +new_data = []; + +end + diff --git a/toolbox/mci/gradients/spm_mci_flow_sun.m b/toolbox/mci/gradients/spm_mci_flow_sun.m new file mode 100644 index 00000000..10d68079 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_flow_sun.m @@ -0,0 +1,24 @@ +function [f, flag, new_data] = spm_mci_flow_sun (t, x, data) +% Evaluate flow for Sundials routines +% FORMAT [f, flag, new_data] = spm_mci_flow_sun (t, x, data) +% +% t time +% x state +% data .U inputs, .P parameters, .M model +% +% f flow, dx/dt +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_flow_sun.m 6548 2015-09-11 12:39:47Z will $ + +U=data.U; +P=data.P; +M=data.M; + +P=spm_unvec(P,M.pE); + +f = spm_mci_flow_t(t,x,U,P,M); +flag = 0; +new_data = []; diff --git a/toolbox/mci/gradients/spm_mci_flow_t.m b/toolbox/mci/gradients/spm_mci_flow_t.m new file mode 100644 index 00000000..f752389c --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_flow_t.m @@ -0,0 +1,27 @@ +function [dxdt] = spm_mci_flow_t (t,x,U,P,M) +% Evaluate flow at time t +% FORMAT [dxdt] = spm_mci_flow_t (t,x,U,P,M) +% +% t time +% x state +% U inputs +% P parameters +% M model +% +% dxdt flow, dx/dt +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_flow_t.m 6548 2015-09-11 12:39:47Z will $ + +% Find nearest time point for which we have pre-computed input +% (We could also compute input on the fly) +if isempty(U) + ut=[]; +else + [tmp,ind]=min(abs(t-M.t)); + ut=U(:,ind); +end + +dxdt=feval(M.f,x,ut,P,M); diff --git a/toolbox/mci/gradients/spm_mci_fwd.m b/toolbox/mci/gradients/spm_mci_fwd.m new file mode 100644 index 00000000..e672c3ee --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_fwd.m @@ -0,0 +1,95 @@ +function [y,x,st] = spm_mci_fwd (P,M,U) +% Integrate dynamics and apply observation model +% FORMAT [y,x,st] = spm_mci_fwd (P,M,U) +% +% P Parameters +% M Model structure +% U Inputs [Nin x N] +% +% y Outputs [N x Nout] +% x States [N x Nstates] +% ... evaluated at the N time points in M.t +% st status flag (0 for OK, -1 for problem) +% +% M.f Flow function dx/dt=f(x,u,P,M) +% M.g Observation function y=g(x,u,P,M) +% M.int Integrator option +% eg. 'euler', 'ode15', 'sundials' +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_fwd.m 6548 2015-09-11 12:39:47Z will $ + +st=0; +y=[];sy=[];x=[];sx=[]; + +try csy=csy; catch csy=0; end +try csx=csx; catch csx=0; end + +% Tolerances for ode15s and SUNDIALS +try reltol=M.reltol; catch reltol=1e-2; end +try abstol=M.abstol; catch abstol=1e-4; end + +tDur=[0 M.T]; + +if isstruct(U) + U=U.u'; +end +if isempty(U) + U=zeros(1,M.N); +end + +switch lower(M.int) + + case 'euler', + x(:,1)=M.x0; + dt=M.T/M.N; + for n=2:M.N, + dxdt=feval(M.f,x(:,n-1),U(:,n-1),P,M); + x(:,n)=x(:,n-1)+dxdt*dt; + end + + case 'ode15', + opto = odeset('RelTol',reltol,'AbsTol',abstol); + sol = ode15s( @(t,x) spm_mci_flow_t(t,x,U,P,M),tDur,M.x0,opto); + x=deval(sol,M.t); + + case 'ode113', + opto = odeset('RelTol',reltol,'AbsTol',abstol); + sol = ode113( @(t,x) spm_mci_flow_t(t,x,U,P,M),tDur,M.x0,opto); + x=deval(sol,M.t); + + case 'sundials', + data.U=U; + data.P=P; + data.M=M; + + % Refer to the Sundials manual for detailed information on the various integration options + % e.g. page 8 of sundials/doc/sundialsTB.pdf + options = CVodeSetOptions('UserData',data,'RelTol',reltol,'AbsTol',abstol, 'LinearSolver','Dense'); + %options = CVodeSetOptions('UserData',data,'RelTol',reltol,'AbsTol',abstol, 'LinearSolver','GMRES'); + CVodeInit(@spm_mci_flow_sun, 'BDF', 'Newton', tDur(1), M.x0, options); + + + [st,time,x] = CVode(M.t,'Normal'); + CVodeFree; + if st==-1 + disp('Problem in spm_mci_fwd'); + return + end + + otherwise + disp(sprintf('Error in spm_mci_fwd.m: Unknown integrator %s ',M.int)); + return +end + +for n=1:M.N, + y(:,n) = feval (M.g,x(:,n),U(:,n),P,M); +end + +y=y'; +x=x'; + + + diff --git a/toolbox/mci/gradients/spm_mci_grad_curve.m b/toolbox/mci/gradients/spm_mci_grad_curve.m new file mode 100644 index 00000000..57d62365 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_grad_curve.m @@ -0,0 +1,91 @@ +function [dLdp,iCpY,st] = spm_mci_grad_curve (assign,w,v,M,U,Y,fxtype) +% Compute gradient and curvature for MFX model +% FORMAT [dLdp,iCpY,st] = spm_mci_grad_curve (assign,w,v,M,U,Y,fxtype) +% +% assign fields specify which are random/fixed effects +% w random effects vector +% v fixed effects vector +% M,U,Y structure,inputs,data +% fxtype 'random' or 'fixed' +% +% dLdp gradient +% iCpY curvature (Fisher information) +% st -1 for integration problem +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_grad_curve.m 6548 2015-09-11 12:39:47Z will $ + +% Extract init and flow params from rfx or ffx vectors +[p_init,p_flow] = spm_mci_init_flow (assign,w,v,M); + +Np=0; +st=0; + +% Gradient and curvature of log likelihood +if strcmp(assign.init_par,fxtype) + [G,sy_init,st] = spm_mci_sens_init (p_init,p_flow,M,U); + sinit=1; + Np=Np+size(sy_init,3); +else + sinit=0; +end + +if strcmp(assign.flow_par,fxtype) + M.x0=p_init; % Initial conditions + [G,sy_flow,st] = spm_mci_sens (p_flow,M,U); + sflow=1; + Np=Np+size(sy_flow,3); +else + sflow=0; +end + +if strcmp(assign.out_par,fxtype) + M.x0=p_init; % Initial conditions + % Compute sensitivity to output params + [G,x,st] = spm_mci_fwd (p_flow,M,U); + for n=1:M.N, + [yout,dydx,dydoutp] = feval (M.g,x(n,:)',U(:,n),p_flow,M); + sy_out(n,:,:)=dydoutp; + end + sout=1; + Np=Np+size(sy_out,3); +else + sout=0; +end + +% Read data points and time indices +try ind=Y.ind; catch ind=1:M.N; end +Nt=length(ind); +y=Y.y; + +if st==-1, disp('Integration Problem !'); return; end + +% Prediction errors +g=G(ind,:); +e=Y.y-g; + +% Compute gradient and precision +dLdp=zeros(1,Np); +iCpY=zeros(Np,Np); +for t=1:Nt, + n=ind(t); + sn=[]; + if sinit + sn=squeeze(sy_init(n,:,:)); + if M.l==1, sn=sn'; end + end + if sflow + sy=squeeze(sy_flow(n,:,:)); + if M.l==1, sy=sy'; end + sn=[sn,sy]; + end + if sout + sy=squeeze(sy_out(n,:,:)); + if M.l==1, sy=sy'; end + sn=[sn,sy]; + end + dLdp=dLdp+e(t,:)*M.iCe*sn; + iCpY=iCpY+sn'*M.iCe*sn; +end diff --git a/toolbox/mci/gradients/spm_mci_joint.m b/toolbox/mci/gradients/spm_mci_joint.m new file mode 100644 index 00000000..ab6b0028 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_joint.m @@ -0,0 +1,36 @@ +function [L,L2,st] = spm_mci_joint (Pr,M,U,Y,beta) +% Compute log joint probability of model +% FORMAT [L,L2,st] = spm_mci_joint (Pr,M,U,Y,beta) +% +% Pr parameters (vectorised and in M.V subspace) +% M model structure +% U inputs +% Y data +% beta inverse temperature +% +% L beta * log p(Y|P) + log p(P) +% L2 log p(Y|P) +% st status flag (0 for OK, -1 for problem) +% +% A default beta=1 gives usual log joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_joint.m 6548 2015-09-11 12:39:47Z will $ + +st=0; +if nargin < 5 | isempty(beta) + beta=1; +end +Pr=Pr(:); + +% Parameter errors in subspace +e = Pr; +L1 = - e'*M.ipC*e/2 + M.log_prior_t2; + +% Parameters in original space +P = M.V*Pr+M.vpE; + +[L2,tmp,st] = feval(M.L,P,M,U,Y); +L = L1+beta*L2; \ No newline at end of file diff --git a/toolbox/mci/gradients/spm_mci_joint_grad.m b/toolbox/mci/gradients/spm_mci_joint_grad.m new file mode 100644 index 00000000..118acd98 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_joint_grad.m @@ -0,0 +1,75 @@ +function [j,iCpY,st,L,L2] = spm_mci_joint_grad (Pr,M,U,Y) +% Gradient of Log Joint Probability +% FORMAT [j,iCpY,st,L,L2] = spm_mci_joint_grad (Pr,M,U,Y) +% +% Pr parameters (vectorised and in M.V subspace) +% M model structure. If field .beta is specified this +% sets the inverse temperature to beta (default=1) +% U inputs +% Y data +% +% j gradient of log joint, dL/dP +% iCpY Curvature (Fisher Information) +% st Status flag (0 for OK, -1 for problem) +% L log joint, L = log p(Y,P) +% L2 log likelihood, L2 = log p(Y|P) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_joint_grad.m 6548 2015-09-11 12:39:47Z will $ + +try beta=M.beta; catch beta=1; end + +st=0; +Pr=Pr(:); + +dLdp_prior = spm_mci_gprior_deriv (Pr,M); + +% Parameters in original space +P = M.V*Pr+M.vpE; + +% g gradient of log likelihood, d[log p(Y|P)]/dP +if nargout > 1 + if isfield(M,'dL') + % User specified routines + [g,iCpY,L2] = feval(M.dL,P,M,U,Y); + % Project into eigenparam space + g=M.V'*g(:); + g=g'; + iCpY=M.V'*iCpY*M.V; + else + if isfield(M,'f') + % For dynamical models use forward sensitivity approach + [g,iCpY,st,L2] = spm_mci_glike_deriv (P,M,U,Y); + else + % For other models use finite differences + [g,iCpY,L2] = spm_mci_diff(P,M,U,Y); + end + end + iCpY=beta*iCpY; +else + if isfield(M,'dL') + % User specified routines + g = feval(M.dL,P,M,U,Y); + % Project into eigenparam space + g=M.V'*g(:); + g=g'; + else + if isfield(M,'f') + % For dynamical models use forward sensitivity approach + g = spm_mci_glike_deriv (P,M,U,Y); + else + % For other models use finite differences + g = spm_mci_diff(P,M,U,Y); + end + end +end + +j = dLdp_prior + beta*g; + +if nargout > 3 + e = Pr; + L1 = - e'*M.ipC*e/2 + M.log_prior_t2; + L = L1+beta*L2; +end diff --git a/toolbox/mci/gradients/spm_mci_sens.m b/toolbox/mci/gradients/spm_mci_sens.m new file mode 100644 index 00000000..caefd41c --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_sens.m @@ -0,0 +1,141 @@ +function [y,sy,st,x,sx] = spm_mci_sens (P,M,U,csx) +% Integrate dynamics, apply observation model and compute sensitivities +% FORMAT [y,sy,st,x,sx] = spm_mci_sens (P,M,U,csx) +% +% P Parameters +% M Model structure +% U Inputs [Nin x N] +% csx Set to 1 to compute state sensitivity +% +% y Outputs [N x Nout] +% sy Output Sensitivity, dy/dP [N x Nout x Nparams] +% st Status flag (0 for OK, -1 for problem) +% x States [N x Nstates] +% sx State Sensitivity, dx/dP [N x Nstates x Nparams] +% ... evaluated at the N time points in M.t +% +% M.f Flow function dx/dt=f(x,u,P,M) +% M.g Observation function y=g(x,u,P,M) +% +% This function uses Matlab's ODE suite +% +% B. Sengupta, K. Friston and W. Penny (2014) Efficient Gradient +% Computation for Dynamical Models. Neuroimage,98, 521-527. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_sens.m 6548 2015-09-11 12:39:47Z will $ + +%disp('Warning: spm_mci_sens.m needs fixing for M.V not square !'); + +y=[];sy=[];x=[];sx=[]; +st=0; + +try csx=csx; catch csx=0; end + +% Tolerances for ode15s +try tol.rel=M.reltol; catch tol.rel=1e-2; end +try tol.abs=M.abstol; catch tol.abs=1e-4; end + +if isstruct(U) + U=U.u'; +end +if isempty(U) + U=zeros(1,M.N); +end + +init_t=0; +final_t=M.T; +init_state=M.x0; + +% parameters for the integrator +options = odeset('AbsTol',tol.abs,'RelTol',tol.rel); + +% Allocate matrices +x=zeros(M.N,M.n); +y=zeros(M.N,M.l); + +try Np = M.Npflow; catch Np=length(spm_vec(P)); end +sx=zeros(M.N,M.n,Np); +sy=zeros(M.N,M.l,Np); + +% initialise state-sensitivites +Nx=length(M.x0); +init_sens = zeros(Nx*Np,1); + +% Use Klopfenstein-Shampine integrator from Matlab's ODE suite +[T,V] = ode15s(@(t,v) integrate_states(t,v,U,P,M),[init_t final_t], [init_state; init_sens], options); + +% T Times +% V States and sensitivities + +% Extract states +xm=V(:,1:Nx); + +% Interpolate states to times M.t +x=interp1q(T,xm,M.t); + +% Interpolate state sensitivities to times M.t +sxm=interp1q(T,V(:,Nx+1:end),M.t); +sx=reshape(sxm,length(M.t),Nx,Np); + +% Compute output and output sensitivity +for n=1:M.N, + [yout,dydx] = feval (M.g,x(n,:)',U(:,n),P,M); + y(n,:) = yout'; + sy(n,:,:)=dydx*squeeze(sx(n,:,:)); +end + +end + +%-------------------------------------------------------------------------- +function DvDt = integrate_states(t,v,U,P,M) +% Integrate states and sensitivities + +Nx=length(M.x0); +try Np = M.Npflow; catch Np=length(spm_vec(P)); end +Nt=Nx+Nx*Np; + +state_ind=1:Nx; +sens_ind=Nx+1:Nt; + +% Find nearest time point for which we have pre-computed input +if isempty(U) + ut=[]; +else + [tmp,ind]=min(abs(t-M.t)); + ut=U(:,ind); +end + +% initialise state matrix +DvDt = zeros(Nt,1); + +% Flow +DvDt(1:Nx)=feval(M.f,v(state_ind),ut,P,M); + +if isfield(M,'dfdx') + Fx = feval(M.dfdx,v(1:Nx),ut,P,M); +else + Fx = spm_diff(M.f,v(1:Nx),ut,P,M,1); +end + +if isfield(M,'dfdp') + Fp = feval(M.dfdp,v(1:Nx),ut,P,M); +else + Fp = spm_diff(M.f,v(1:Nx),ut,P,M,3); +end + +Sx_old = v(sens_ind); +Sx_old = reshape(Sx_old,Nx,Np); + +% Gronwall's Theorem +Sx = Fx*Sx_old+Fp; + +% Sensitivities +DvDt(sens_ind)=Sx(:); + +end + + + diff --git a/toolbox/mci/gradients/spm_mci_sens_init.m b/toolbox/mci/gradients/spm_mci_sens_init.m new file mode 100644 index 00000000..4fe7c4f1 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_sens_init.m @@ -0,0 +1,131 @@ +function [y,sy,st] = spm_mci_sens_init (R,P,M,U) +% Compute sensitivity to initial state +% FORMAT [y,sy,st] = spm_mci_sens_init (R,P,M,U) +% +% R Initial state +% P Parameters +% M Model structure +% U Inputs [Nin x N] +% +% y Outputs [N x Nout] +% sy Output Sensitivity, dy/dP [N x Nout x Nparams] +% st Status flag (0 for OK, -1 for problem) +% ... evaluated at the N time points in M.t +% +% M.f Flow function dx/dt=f(x,u,P,M) +% M.g Observation function y=g(x,u,P,M) +% +% This function uses Matlab's ODE suite +% +% B. Sengupta, K. Friston and W. Penny (2014) Efficient Gradient +% Computation for Dynamical Models. Neuroimage,98, 521-527. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_sens_init.m 6548 2015-09-11 12:39:47Z will $ + +y=[];sy=[];x=[];sx=[]; +st=0; + +% Tolerances for ode15s +try tol.rel=M.reltol; catch tol.rel=1e-2; end +try tol.abs=M.abstol; catch tol.abs=1e-4; end + +if isempty(U) + U=zeros(1,M.N); +end + +init_t=0; +final_t=M.T; +init_state=R; + +% parameters for the integrator +options = odeset('AbsTol',tol.abs,'RelTol',tol.rel); + +% Allocate matrices +x=zeros(M.N,M.n); +y=zeros(M.N,M.l); +sx=zeros(M.N,M.n,M.n); +sy=zeros(M.N,M.l,M.n); + +% initialise state-sensitivites +Nx=length(M.x0); +Np=length(P); +S0=eye(M.n); +init_sens = S0(:); + +% Use Klopfenstein-Shampine integrator from Matlab's ODE suite +[T,V] = ode15s(@(t,v) int_states_init(t,v,U,P,M),[init_t final_t], [init_state; init_sens], options); + +% T Times +% V States and sensitivities + +% Extract states +xm=V(:,1:Nx); + +% Interpolate states to times M.t +x=interp1q(T,xm,M.t); + +% Interpolate state sensitivities to times M.t +sxm=interp1q(T,V(:,Nx+1:end),M.t); +%sx=reshape(sxm,length(M.t),Nx,Np); +% Updated Sep 13 2014 +sx=reshape(sxm,length(M.t),Nx,Nx); + +% When computing output sensitivities, assume dydx=L, ie not a +% function of x. Generalise later +[tmp,L]=feval(M.g,M.x0,U(:,1),P,M); + +% Compute output and output sensitivity +for n=1:M.N, + yout = feval (M.g,x(n,:)',U(:,n),P,M); + y(n,:) = yout'; + sy(n,:,:)=L*squeeze(sx(n,:,:)); +end + +end + +%-------------------------------------------------------------------------- +function DvDt = int_states_init (t,v,U,P,M) +% Integrate states and sensitivities to initial conditions + +Nx=length(M.x0); +Nt=Nx+Nx*Nx; + +state_ind=1:Nx; +sens_ind=Nx+1:Nt; + +% Find nearest time point for which we have pre-computed input +if isempty(U) + ut=[]; +else + [tmp,ind]=min(abs(t-M.t)); + ut=U(:,ind); +end + +% initialise state matrix +DvDt = zeros(Nt,1); + +% Flow +DvDt(1:Nx)=feval(M.f,v(state_ind),ut,P,M); + +if isfield(M,'dfdx') + Fx = feval(M.dfdx,v(1:Nx),ut,P,M); +else + Fx = spm_diff(M.f,v(1:Nx),ut,P,M,1); +end + +Sx_old = v(sens_ind); +Sx_old = reshape(Sx_old,Nx,Nx); + +% Compute change in sensitivity from old - note absence of Fp term +Sx = Fx*Sx_old; + +% Sensitivities +DvDt(sens_ind)=Sx(:); + +end + + + diff --git a/toolbox/mci/gradients/spm_mci_sens_sun.m b/toolbox/mci/gradients/spm_mci_sens_sun.m new file mode 100644 index 00000000..9985ab04 --- /dev/null +++ b/toolbox/mci/gradients/spm_mci_sens_sun.m @@ -0,0 +1,118 @@ +function [y,sy,st,x,sx] = spm_mci_sens_sun (P,M,U,csx) +% As spm_mci_sens.m but using Sundials +% FORMAT [y,sy,st,x,sx] = spm_mci_sens_sun (P,M,U,csx) +% +% P Parameters +% M Model structure +% U Inputs [Nin x N] +% csx Set to 1 to compute state sensitivity +% +% y Outputs [N x Nout] +% sy Output Sensitivity, dy/dP [N x Nout x Nparams] +% st Status flag (0 for success, -1 for problem) +% x States [N x Nstates] +% sx State Sensitivity, dx/dP [N x Nstates x Nparams] +% ... evaluated at the N time points in M.t +% +% M.f Flow function dx/dt=f(x,u,P,M) +% M.g Observation function y=g(x,u,P,M) +% +% This function uses the sundials package (CVODE,CVODES,IDA,IDAS) +% from http://computation.llnl.gov/casc/sundials/main.html +% +% B. Sengupta, K. Friston and W. Penny (2014) Efficient Gradient +% Computation for Dynamical Models. Neuroimage,98, 521-527. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_sens_sun.m 6548 2015-09-11 12:39:47Z will $ + +y=[];sy=[];x=[];sx=[]; +st=0; + +try csx=csx; catch csx=0; end + +% Tolerances for ode15s and SUNDIALS +try reltol=M.reltol; catch reltol=1e-2; end +try abstol=M.abstol; catch abstol=1e-4; end + +tDur=[0 M.T]; +%tDur=[M.t(1) M.T]; + +if isstruct(U) + U=U.u'; +end +if isempty(U) + U=zeros(1,M.N); +end + +data.U=U; +data.P=P; +data.M=M; + +options = CVodeSetOptions('UserData',data,'RelTol',reltol,'AbsTol',abstol, 'LinearSolver','Dense'); +CVodeInit(@spm_mci_flow_sun, 'BDF', 'Newton', tDur(1), M.x0, options); + +sens_t = zeros(M.n,M.Np); + +FSAoptions = CVodeSensSetOptions('method','Simultaneous',... + 'ErrControl', true,... + 'ParamField', 'P',... + 'ParamScales', ones(1,M.Np)); + +CVodeSensInit(M.Np, [], sens_t, FSAoptions); + +time(1,1) = tDur(1); +xm(:,1) = M.x0; +smx(1,:,:) = sens_t; +smy(1,:,:) = zeros(M.l,M.Np); + +% When computing output sensitivities, assume dydx=L, ie not a +% function of x. Generalise later +[tmp,L]=feval(M.g,M.x0,U(:,1),P,M); + +ti = tDur(1); +it = 1; +while ti < tDur(2) + it = it+1; + [status, ti, xt, yS] = CVode(tDur(2),'OneStep'); + if status==-1 + disp('Problem in spm_mci_sens_sun.m'); + st=-1; + return + end + time(it,1) = ti; + xm(:,it) = xt; + if csx + smx(it,:,:) = yS; + end + % Edit here if dydx is a function of x + smy(it,:,:) = L*yS; +end + +% Interpolate to M.t +x=interp1(time,xm',M.t,'spline'); +x=x'; +if csx + sx=zeros(M.N,M.n,M.Np); + for j=1:M.Np, + sx(:,:,j)=interp1q(time,squeeze(smx(:,:,j)),M.t); + end +end +sy=zeros(M.N,M.l,M.Np); +for j=1:M.Np, + sy(:,:,j)=interp1q(time,squeeze(smy(:,:,j)),M.t); +end + +CVodeFree; + +for n=1:M.N, + y(:,n) = feval (M.g,x(:,n),U(:,n),P,M); +end + +y=y'; +x=x'; + + + diff --git a/toolbox/mci/inference/spm_mci_ais.m b/toolbox/mci/inference/spm_mci_ais.m new file mode 100644 index 00000000..15e1552b --- /dev/null +++ b/toolbox/mci/inference/spm_mci_ais.m @@ -0,0 +1,172 @@ +function [post] = spm_mci_ais (mcmc,M,U,Y,vl) +% Annealed Importance Sampling +% FORMAT [post] = spm_mci_ais (mcmc,M,U,Y,vl) +% +% mcmc Optimisation parameters eg. +% +% .J number of temperatures +% .anneal annealing schedule: +% 'sigmoid', 'linear', 'nonlinear', 'log' or 'power' +% .prop type of proposal: 'lmc' or 'mh' (default) +% .nprop number of proposals at each temperature +% .maxits number of independent samples to produce +% +% M Model structure +% U Input structure +% Y Data +% vl Variational Laplace solution +% .Ep Posterior Mean +% .Cp Posterior Covariance +% If this field is specified then AIS starts sampling +% from the VL posterior. Otherwise from the model prior. +% +% The function returns data structure 'post' with fields +% +% .P P(:,j) is jth posterior sample +% .logev approximation to log evidence +% .logev_se standard error thereof +% .logev_lower 5th percentile thereof +% .logev_upper 95th percentile thereof +% .logev_resample resampled log evidences +% .traj individual trajectories +% .acc acceptances +% .logw log of (unnormalised) importance weights +% .q normalised importance weights +% .E energy (negative log joint) +% .beta set of inverse temperatures +% +% R Neal (2001) Annealed Importance Sampling. Statistics and +% Computing, 11, 125-139. +% +% This implementation uses the Matlab Parallel Computing toolbox +% (see use of parfor instead of for below). +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_ais.m 6548 2015-09-11 12:39:47Z will $ + +try J=mcmc.J; catch J=32; end +try anneal=mcmc.anneal; catch anneal='power'; end +try prop=mcmc.prop; catch prop='mh'; end +try maxits=mcmc.maxits; catch maxits=32; end +try nprop=mcmc.nprop; catch nprop=5; end +try verbose=mcmc.verbose; catch verbose=1; end +try vl=vl; catch vl=[]; end; +try mcmc.rec_traj; catch mcmc.rec_traj=0; end + +nprop=nprop+1; + +M = spm_mci_minit (M); +V = M.V; +Np = size(V,1); +Nsub = size(V,2); + +if verbose + disp(sprintf('Using %s annealing schedule with %d temperatures',anneal,J)); +end + +% Set annealing schedule +switch anneal + case 'sigmoid', + % Good for model switching integration + x=linspace(-10,10,J); + beta=1./(1+exp(-x)); + case 'linear', + beta=linspace(0,1,J); + case 'geometric', + beta=logspace(log10(1/J),0,J-1); + beta=[0 beta]; + case 'nonlinear', + eta=0.2; + j=1:J; + beta=(eta*j/J)./(1-j/J+eta); + case 'power', + % From Calderhead & Girolami + beta=linspace(0,1,J); + beta=beta.^5; + case 'frozen', + beta=ones(1,J); + otherwise + disp('Unknown type of annealing schedule in spm_mci_ais.m'); + return +end + +mcmc.scale=fliplr(10.^-[0:1:nprop-1]); +mcmc.beta=beta; +mcmc.nprop=nprop; + +if ~isempty(vl) + vl.Cp=full(vl.Cp); + vl.Lambdap=inv(vl.Cp); + vl.const=-0.5*Np*log(2*pi)-0.5*spm_logdet(vl.Cp); + % Mean and cov in subspace: + vl.mr=M.V'*vl.Ep-M.vpE + vl.Cr=M.V'*vl.Cp*M.V; +end + +parfor i=1:maxits, + if verbose + disp(sprintf('Acquiring %d out of %d IID samples',i,maxits)); + end + + if ~isempty(vl) + [P(:,i),E(i),logw(i),acc(i,:),traj(i,:,:)] = spm_mci_ais_single_vl (mcmc,M,U,Y,vl); + else + [P(:,i),E(i),logw(i),acc(i,:),traj(i,:,:)] = spm_mci_ais_single (mcmc,M,U,Y); + end +end + +% Model evidence +[logev,q] = ais_evidence (logw); + +% Now get standard error and lower/upper 90% +% confidence interval from bootstrapping +Nboot=1000; +for b=1:Nboot, + ind=ceil(rand(1,maxits)*maxits); + logw_resample=logw(ind); + logev_resample(b)=ais_evidence(logw_resample); +end +logev_se=std(logev_resample); +lind=ceil(0.05*Nboot); +uind=floor(0.95*Nboot); +lsort=sort(logev_resample); +logev_low=lsort(lind); +logev_high=lsort(uind); + +% Project parameters from eigenspace of prior back into original space +nj=size(P,2); +P=spm_vec(M.pE)*ones(1,nj)+V*P; + +% Get Posterior Mean using importance weights +post.wEp=P*q'; + +post.P=P; +post.logev=logev; +post.logev_se=logev_se; +post.logev_lower=logev_low; +post.logev_upper=logev_high; +post.logev_resample=logev_resample; +post.acc=acc; +post.traj=traj; +post.q=q; +post.E=E; +post.beta=beta; +post.ind=[1:maxits]; +post.logw=logw; + +end + +%------------------------------------------------------- + +function [logev,q] = ais_evidence (logw) + +r=max(logw); +u=exp(logw-r); +S=mean(u); +logev=log(S)+r; +q=u/sum(u); + +end + diff --git a/toolbox/mci/inference/spm_mci_ais_single.m b/toolbox/mci/inference/spm_mci_ais_single.m new file mode 100644 index 00000000..bf44d13d --- /dev/null +++ b/toolbox/mci/inference/spm_mci_ais_single.m @@ -0,0 +1,100 @@ +function [P,E,logw,acc,traj] = spm_mci_ais_single (mcmc,M,U,Y) +% Produce a single independent sample using AIS +% FORMAT [P,E,logw,acc,traj] = spm_mci_ais_single (mcmc,M,U,Y) +% +% mcmc Sampling settings +% M Model structure +% U Input structure +% Y Data +% +% P [Np x 1] sample +% E Negative log joint +% logw Contribution to model evidence +% acc acc(j) is acceptance rate at temperature j +% traj traj(p,j) is value of parameter p at temperature j +% (only set if mcmc.rec_traj=1) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_ais_single.m 6548 2015-09-11 12:39:47Z will $ + +beta=mcmc.beta; +nprop=mcmc.nprop; +prop=mcmc.prop; +scale=mcmc.scale; + +J=length(beta); + +if isstruct(M.pC) + pC = full(diag(spm_vec(M.pC))); +else + pC = M.pC; +end +% prior cov in subspace +pC=M.V'*pC*M.V; + +Np=size(pC,1); + +% Sample from prior (in subspace) +x(:,1) = spm_normrnd(zeros(Np,1),pC,1); +[L(1),L2] = spm_mci_joint (x(:,1),M,U,Y); +Lsum = (beta(2)-beta(1))*L2; +acc(1) = 1; +for j=2:J, + xs=x(:,j-1); Ls=L(:,j-1);acc(j)=0; + switch prop, + case 'mh', + % Generate sample at next temperature by composing + % Metropolis moves at different scales + for s=1:nprop, + dx=spm_normrnd(zeros(Np,1),scale(s)^2*pC,1); + xcand=xs+dx; + [Lcand,L2cand] = spm_mci_joint (xcand,M,U,Y,beta(j)); + dL = Lcand-Ls; + r = exp(dL); + alpha = min(1,r); + test_prob = rand(1); + if alpha > test_prob + % Accept + xs = xcand; + Ls = Lcand; + L2 = L2cand; + acc (j) = 1; + end + end + + case 'lmc', + % Generate sample at next temperature using + % Langevin Monte Carlo + M.beta=beta(j); + lgv_mcmc.init=xs; + lgv_mcmc.maxits=nprop; + lgv_mcmc.update_obs_noise=0; + + [tmp,stats] = spm_mci_lgv (lgv_mcmc,M,U,Y); + + xs = stats.P(:,end); + Ls = -stats.E(:,end); + L2 = stats.L2(end); + acc (j) = any(stats.acc(2:end)); + + otherwise + disp('Unknown proposal type in spm_mci_ais_single.m'); + end + x(:,j)=xs; + L(j)=Ls; + if j < J + Lsum = Lsum + (beta(j+1)-beta(j))*L2; + end +end +logw=Lsum; +P=x(:,J); +E=-L(J); + +if mcmc.rec_traj + nj=size(x,2); + traj=spm_vec(M.pE)*ones(1,nj)+M.V*x; +else + traj=[]; +end diff --git a/toolbox/mci/inference/spm_mci_ais_single_vl.m b/toolbox/mci/inference/spm_mci_ais_single_vl.m new file mode 100644 index 00000000..0e33210d --- /dev/null +++ b/toolbox/mci/inference/spm_mci_ais_single_vl.m @@ -0,0 +1,74 @@ +function [P,E,logw,acc] = spm_mci_ais_single_vl (mcmc,M,U,Y,vl) +% Produce a single independent sample from posterior using AIS +% FORMAT [P,E,logw,acc] = spm_mci_ais_single_vl (mcmc,M,U,Y,vl) +% +% mcmc Sampling settings +% M Model structure +% U Input structure +% Y Data +% vl Variational Laplace solution +% .Ep Posterior Mean +% .Cp Posterior Covariance +% +% P [Np x 1] sample +% E Negative log joint +% logw Contribution to model evidence +% acc acc(j) is acceptance rate at temperature j +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_ais_single_vl.m 6548 2015-09-11 12:39:47Z will $ + +beta=mcmc.beta; +nprop=mcmc.nprop; +prop=mcmc.prop; +scale=mcmc.scale; + +J=length(beta); + +% Sample from VL posterior (in subspace) +x(:,1) = spm_normrnd (vl.mr,vl.Cr,1); +[L(1),L2] = spm_mci_joint (x(:,1),M,U,Y); + +% Sample in original space +xorg = M.V*x(:,1)+M.vpE; +e = xorg - vl.Ep; +log_pvl = vl.const - 0.5*e'*vl.Lambdap*e; +Lsum = (beta(1)-beta(2))*log_pvl+(beta(2)-beta(1))*L(1); + +acc(1) = 1; +for j=2:J, + xs=x(:,j-1); Ls=L(:,j-1);acc(j)=0; + + % Generate sample at next temperature using + % Langevin Monte Carlo + lgv_mcmc.init=xs; + lgv_mcmc.maxits=nprop; + + [tmp,stats] = spm_mci_lgv_vl (lgv_mcmc,M,U,Y,vl,beta(j)); + + xs = stats.P(:,end); + Ls = -stats.E(:,end); + L2 = stats.L2(end); + acc (j) = any(stats.acc(2:end)); + + x(:,j)=xs; + L(j)=Ls; + if j < J + xorg = M.V*x(:,j)+M.vpE; + e = xorg - vl.Ep; + log_pvl = vl.const - 0.5*e'*vl.Lambdap*e; + Lsum = Lsum + (beta(j)-beta(j+1))*log_pvl+(beta(j+1)-beta(j))*L(j); + end +end +logw=Lsum; +P=x(:,J); +E=-L(J); + +if mcmc.rec_traj + nj=size(x,2); + traj=spm_vec(M.pE)*ones(1,nj)+V*x; +else + traj=[]; +end \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_check.m b/toolbox/mci/inference/spm_mci_check.m new file mode 100644 index 00000000..6fceee83 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_check.m @@ -0,0 +1,84 @@ +function [corr] = spm_mci_check (M) +% Check model structure M is correctly specified +% FORMAT [corr] = spm_mci_check (M) +% +% corr 1 for correctly specified model +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_check.m 6548 2015-09-11 12:39:47Z will $ + +corr=1; +if ~isfield(M,'N') + disp('Number of time points M.N not specified'); + corr=0; +end +if ~isfield(M,'T') + disp('Total time M.T not specified'); + corr=0; +end +if ~isfield(M,'t') + disp('Time point vector M.t not specified'); + corr=0; +end +if ~isfield(M,'m') + disp('Number of inputs M.m not specified'); + corr=0; +end +if ~isfield(M,'n') + disp('Number of states M.n not specified'); + corr=0; +end +if ~isfield(M,'l') + disp('Number of outputs M.l not specified'); + corr=0; +end +if ~isfield(M,'x0') + disp('Initial state M.x0 not specified'); + corr=0; +end +if ~isfield(M,'x') + disp('Reshaped state template M.x not specified'); + corr=0; +end +if ~isfield(M,'f') + disp('Flow function M.f not specified'); + corr=0; +end +if ~isfield(M,'g') + disp('Observation function M.g not specified'); + corr=0; +end +if ~isfield(M,'int') + disp('Integration method M.int not specified'); + corr=0; +end +if ~isfield(M,'Np') + disp('Number of parameters M.Np not specified'); + corr=0; +end +if ~isfield(M,'pE') + disp('Prior mean M.pE not specified'); + corr=0; +end +if ~isfield(M,'pC') + disp('Prior covariance M.pC not specified'); + corr=0; +end +if ~isfield(M,'ipC') + disp('Inverse prior covariance M.ipC not specified'); + corr=0; +end +if ~isfield(M,'L') + disp('Likelihood function M.L not specified'); + corr=0; +end +if ~isfield(M,'Ce') + disp('Observation noise covariance M.Ce not specified'); + corr=0; +end + +if corr + disp('Model is correctly specified'); +end \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_diag.m b/toolbox/mci/inference/spm_mci_diag.m new file mode 100644 index 00000000..7afdd848 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_diag.m @@ -0,0 +1,136 @@ +function [mess] = spm_mci_diag (post,diag) +% Monte Carlo Diagnostics +% FORMAT [mess] = spm_mci_diag (post,diag) +% +% post posterior distribution +% diag diagnostic info +% .ind indices of samples to look at +% .traceplot (1/0) for trace plots +% .autoplot (1/0) for autocorrelations +% .essplot (1/0) for effective sample sizes +% .eplot (1/0) for energy (neg log joint) traj +% .bplot (1/0) for Bayes factor of f/b transitions +% +% ess effective sample size (for each parameter) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_diag.m 6548 2015-09-11 12:39:47Z will $ + +Ns=size(post.P,2); +try traceplot=diag.traceplot; catch traceplot=1; end +try autoplot=diag.autoplot; catch autoplot=0; end +try essplot=diag.essplot; catch essplot=0; end +try eplot=diag.eplot; catch eplot=1; end +try bplot=diag.bplot; catch bplot=1; end +try ind=diag.ind; catch ind=[1:Ns]; end + +P=post.P(:,ind); + +[Np,Ns]=size(P); +rp=ceil(sqrt(Np)); + +if eplot + h=figure; + set(h,'Name','Energy Trajectory'); + %plot(post.Etraj(ind)); + set(gca,'FontSize',16); + semilogx(post.E(ind),'k'); + grid on + xlabel('Sample'); + ylabel('Energy'); +end + +if bplot + j=ind(2:end); + + h=figure; + set(h,'Name','Proposal Details'); + %plot(post.bayes_fb(ind)); + + if isfield(post,'bayes_fb') + subplot(2,2,1); + semilogx(post.bayes_fb(j),'k'); + set(gca,'FontSize',16); + grid on + xlabel('Sample'); + ylabel('LogBF (FB)'); + end + + if isfield(post,'dL') + subplot(2,2,3); + semilogx(post.dL(j),'k'); + set(gca,'FontSize',16); + grid on + xlabel('Sample'); + ylabel('dL'); + end + + if isfield(post,'dL') & isfield(post,'bayes_fb') + subplot(2,2,2); + semilogx(post.dL(j)-post.bayes_fb(j),'k'); + set(gca,'FontSize',16); + grid on + xlabel('Sample'); + ylabel('Log r'); + end + + if isfield(post,'acc') + subplot(2,2,4); + semilogx(post.acc(j),'k'); + ylim([-0.1 1.1]); + set(gca,'FontSize',16); + grid on + xlabel('Sample'); + ylabel('Accepted'); + end +end + +if traceplot + % Trace plots for each parameter + h=figure; + set(h,'Name','Parameter Samples'); + for p=1:Np, + subplot(rp,rp,p); + plot(P(p,:),'k'); + %set(gca,'FontSize',16); + xlabel('Sample'); + ylabel(sprintf('P(%d)',p)); + grid on + end +end + +if autoplot + % Autocorrelation plots for each parameter + h=figure; + set(gca,'FontSize',16); + set(h,'Name','Parameter Autocorrelations'); + Nlags=ceil(Ns/10); + for p=1:Np, + subplot(rp,rp,p); + plot(autocorr(P(p,:),Nlags),'k'); + xlabel('Sample'); + ylabel(sprintf('P(%d)',p)); + grid on + end +end + +if essplot + % Effective sample sizes + h=figure; + set(h,'Name','Effective Sample Size'); + for p=1:Np, + ess(p)=spm_mci_ess(P(p,:)); + end + mess=mean(ess); + disp('Mean Effective Sample Size:'); + disp(mess); + + bar(ess); + xlabel('Parameter'); + ylabel('ESS'); +else + mess=[]; +end + diff --git a/toolbox/mci/inference/spm_mci_diff.m b/toolbox/mci/inference/spm_mci_diff.m new file mode 100644 index 00000000..d2e17a8f --- /dev/null +++ b/toolbox/mci/inference/spm_mci_diff.m @@ -0,0 +1,28 @@ +function [dLdp,iCpY,L] = spm_mci_diff (P,M,U,Y) +% Compute gradient and curvature of log likelihood using finite differences +% FORMAT [dLdp,iCpY,L] = spm_mci_diff (P,M,U,Y) +% +% dLdp gradient of log likelihood +% iCpY curvature (observed Fisher Information) +% L log likelihood +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_diff.m 6548 2015-09-11 12:39:47Z will $ + +dLdp = spm_diff(M.L,P,M,U,Y,1); +dLdp = full(dLdp(:))'; + +if nargout > 1 + % Compute Hessian + H=spm_diff(M.L,P,M,U,Y,[1 1]); + Np=length(H); + for p=1:Np, + iCpY(:,p)=-full(H{p}(:)); + end +end + +if nargout > 2 + L = feval(M.L,P,M,U,Y); +end diff --git a/toolbox/mci/inference/spm_mci_ess.m b/toolbox/mci/inference/spm_mci_ess.m new file mode 100644 index 00000000..004ed826 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_ess.m @@ -0,0 +1,60 @@ +function [ess,m] = spm_mci_ess (x,p) +% Compute Effective Sample Size +% FORMAT [ess,m] = spm_mci_ess (x,p) +% +% x Univariate time series +% p Maximum lag for autocovariance estimation +% +% ess Effective Sample Size +% m Number of lags used in ESS estimate +% +% This routine is based on the Initial Positive Sequence estimate +% proposed in C. Geyer (1992) Practical Markov Chain Monte Carlo, +% Statistical Science, 7(4):473-511. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_ess.m 6548 2015-09-11 12:39:47Z will $ + +N=length(x); + +try pmax=p; catch pmax=min(ceil(N/10),256); end + +for i=1:pmax, + y(:,i)=x(pmax-i+1:end-i); +end +C=cov(y); +c=C(1,:); +gamma=c(2:end); +gamma0=c(1); +r=gamma/gamma0; + +G=[]; +for j=1:floor(pmax/2)-1, + % Sum of adjacent pairs of autocovariances + G(j)=gamma(2*j)+gamma(2*j+1); +end + +if ~isempty(G) + % Find minimum j such that all G's up to j are positive + Gneg=find(G<0); + if isempty(Gneg) + m=length(G); + else + m1=min(Gneg); + m=m1-1; + end +else + m=0; +end + +ess=N/(1+2*sum(r(1:2*m))); + +% figure; +% plot(c); +% title('Autocovariance'); +% +% figure +% plot(G); +% title('Sum of adjacent covariances'); \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_fixed.m b/toolbox/mci/inference/spm_mci_fixed.m new file mode 100644 index 00000000..1d2e145b --- /dev/null +++ b/toolbox/mci/inference/spm_mci_fixed.m @@ -0,0 +1,141 @@ +function [Psamp,noise,M] = spm_mci_fixed (mcmc,w,fixed,noise,M,U,Y) +% Group fixed effects estimation +% FORMAT [Psamp,noise,M] = spm_mci_fixed (mcmc,w,fixed,noise,M,U,Y) +% +% mcmc Sampling parameters +% w(:,n) Random effects for nth subject +% fixed [fixed.pE, fixed.pC] prior over fixed effects +% noise noise structure +% M,U,Y Model, input, data structures +% +% Psamp Samples, [maxits x M{1}.Np] +% noise updated noise model +% M updated model structures +% +% Uses Langevin Monte Carlo +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_fixed.m 6548 2015-09-11 12:39:47Z will $ + +try verbose=mcmc.verbose; catch verbose=0; end +try maxits=mcmc.maxits; catch maxits=64; end +try plot_int=mcmc.plot_int; catch plot_int=1; end +try update_obs_step=mcmc.update_obs_step; catch update_obs_step=64; end +try h=mcmc.h; catch h=0.5; end + +assign=mcmc.assign; + +% Prior over fixed effects +pE=fixed.pE; +Np=length(pE); +V=eye(Np); +ipC=inv(fixed.pC); +log_prior_t2=spm_logdet(ipC)/2-0.5*Np*log(2*pi); + +N=length(M); +for n=1:N + M{n}.logdet_Ce=spm_logdet(M{n}.Ce); + M{n}.iCe = inv(M{n}.Ce); +end + +try init=mcmc.init; catch init=fixed.vpE; end +xinit = init; +x = zeros(maxits,Np); +x(1,:) = xinit'; + +if verbose figure; end + +% Tune h by monitoring acceptance rate +tune_h=1; +acc_block=32; +acc_low=0.3; +acc_high=0.7; +total_acc_target=64; % Number of accepted samples to get +acc=zeros(maxits,1); + +i=1; +while (i < maxits) & (sum(acc) < total_acc_target), + + if verbose + if mod(i,plot_int) == 0 & i > 2 + spm_mci_progress (x,E,i); + end + end + + if mod(i,acc_block)==0 & tune_h + % Change step size h ? + Nacc=sum(acc(i-acc_block+1:i-1)); + prop_acc=Nacc/acc_block; + if prop_acc < acc_low + if verbose, disp('Decreasing step size ...'); end + h=h/2; + elseif prop_acc > acc_high + if verbose, disp('Increasing step size ...'); end + h=h*2; + end + end + + % Proposal (first proposal always accepted) + if i==1 + pos=x(1,:)'; + curr=[]; + else + pos=spm_normrnd(curr.mu,curr.Cp,1); + end + + % Quantities re proposal + prop.pos=pos; + + % Get gradient and curvature of log like + dLdp=zeros(1,Np); + iCpY=zeros(Np,Np); + + for n=1:N, + if ~isempty(w), wsub=w(:,n); else, wsub=[]; end + [dLdp_n,iCpY_n,st] = spm_mci_grad_curve (assign,wsub,pos,M{n},U{n},Y{n},'fixed'); + + dLdp = dLdp + dLdp_n; + iCpY = iCpY + iCpY_n; + end + dlogprior = - (pos-pE)'*ipC; + j=dLdp+dlogprior; + + % Log like + log_like=0; + for n=1:N, + if ~isempty(w), wsub=w(:,n); else, wsub=[]; end + [Pinit,Pflow] = spm_mci_init_flow (assign,wsub,pos,M{n}); + like_n = spm_mci_like_ind (Pflow,Pinit,M{n},U{n},Y{n}); + log_like = log_like + like_n; + end + e=pos-pE; + log_prior = - e'*ipC*e/2 + log_prior_t2; + prop.L=log_like+log_prior; + + if st==-1 + disp('Integration problem in spm_mci_random.m'); + keyboard + end + + % Posterior covariance under local linear approximation + prop.Cp = h*inv(iCpY+ipC); + prop.mu = pos+0.5*h*prop.Cp*j(:); + + prop.iCp = inv(prop.Cp); + prop.logdetCp = spm_logdet(prop.Cp); + + % Accept proposal ? + [curr,accepted,bayes_fb(i),dL(i)] = spm_mci_mh_update(curr,prop,verbose); + acc(i)=accepted; + E(i) = -curr.L; + if i > 1 + dEdit(i-1)=100*(E(i)-E(i-1))/E(i-1); + end + x(i,:)=curr.pos; + + i=i+1; +end + +Psamp=x(1:i-1,:); diff --git a/toolbox/mci/inference/spm_mci_glike.m b/toolbox/mci/inference/spm_mci_glike.m new file mode 100644 index 00000000..681e3360 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_glike.m @@ -0,0 +1,39 @@ +function [L,e,st] = spm_mci_glike (P,M,U,Y,G) +% Gaussian Log-likelihood +% FORMAT [L,e,st] = spm_mci_glike (P,M,U,Y,G) +% +% P Parameters +% M Model structure +% U Inputs +% Y Data +% G Predictions (computed if not provided) +% +% L Log Likelihood +% e Errors +% st Status flag (0 for OK, -1 for problem) +% +% Assumes diagonal error covariance M.Ce +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_glike.m 6548 2015-09-11 12:39:47Z will $ + +st=0; +try + G = G; +catch + if isfield(M,'IS') + [G,tmp,st] = feval(M.IS,P,M,U); + else + [G,tmp,st] = spm_mci_fwd (P,M,U); + end +end + +e = Y-G; +d = size(e,2); +L = -0.5*trace(M.iCe*e'*e) - 0.5*M.N*M.logdet_Ce - 0.5*M.N*d*log(2*pi); + +if isnan(L) + L=realmin; +end diff --git a/toolbox/mci/inference/spm_mci_glike_deriv.m b/toolbox/mci/inference/spm_mci_glike_deriv.m new file mode 100644 index 00000000..985ead80 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_glike_deriv.m @@ -0,0 +1,69 @@ +function [dLdp,iCpY,st,L] = spm_mci_glike_deriv (P,M,U,Y) +% Gradient of Gaussian Log-likelihood +% FORMAT [dLdp,iCpY,st,L] = spm_mci_glike_deriv (P,M,U,Y) +% +% P Parameters +% M Model structure +% U Inputs +% Y Data +% +% dLdP Gradient of Log Likelihood wrt params, [1 x Np] +% iCpY Curvature (Fisher Information) +% st status flag (0 for OK, -1 for problem) +% L Log Likelihood +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_glike_deriv.m 6548 2015-09-11 12:39:47Z will $ + +%[G,x,S] = spm_mci_fwd (P,M,U,1); +if strcmp(M.int,'sundials') + [G,S,st] = spm_mci_sens_sun (P,M,U); +else + [G,S,st] = spm_mci_sens (P,M,U); +end +e = Y-G; + +if isfield(M,'Npout') & M.Npout > 0 + % Compute sensitivity to output parameters + [G,x,st] = spm_mci_fwd (P,M,U); + sy_out=zeros(M.N,M.l,M.Npout); + for n=1:M.N, + [tmp1,tmp2,dydoutp] = feval (M.g,x(n,:)',U(:,n),P,M); + sy_out(n,:,:)=dydoutp; + end + Np=M.Npflow+M.Npout; +else + Np=size(S,3); +end + +dLdp=zeros(1,Np); +for n=1:M.N, + sn=squeeze(S(n,:,:)); + if M.l==1, sn=sn'; end + if isfield(M,'Npout') & M.Npout > 0 + sy=squeeze(sy_out(n,:,:)); + if M.l==1, sy=sy'; end + sn=[sn,sy]; + end + dLdp=dLdp+e(n,:)*M.iCe*sn; +end + +if nargout > 1 + iCpY=zeros(Np,Np); + for n=1:M.N, + sn=squeeze(S(n,:,:)); + if M.l==1, sn=sn'; end + if isfield(M,'Npout') & M.Npout > 0 + sy=squeeze(sy_out(n,:,:)); + if M.l==1, sy=sy'; end + sn=[sn,sy]; + end + iCpY=iCpY+sn'*M.iCe*sn; + end +end + +if nargout > 3 + L=spm_mci_glike (P,M,U,Y,G); +end diff --git a/toolbox/mci/inference/spm_mci_gprior_deriv.m b/toolbox/mci/inference/spm_mci_gprior_deriv.m new file mode 100644 index 00000000..2045074b --- /dev/null +++ b/toolbox/mci/inference/spm_mci_gprior_deriv.m @@ -0,0 +1,21 @@ +function [j] = spm_mci_gprior_deriv (Pr,M) +% Gradient of Log Gaussian prior +% FORMAT [j] = spm_mci_gprior_deriv (Pr,M) +% +% Pr parameters (vectorised and in M.V subspace) +% M model structure +% +% j gradient of log Gaussian prior +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_gprior_deriv.m 6548 2015-09-11 12:39:47Z will $ + +Pr=Pr(:); + +% Parameters errors in subspace +e = Pr; + +% Gradient +j = - e'*M.ipC; diff --git a/toolbox/mci/inference/spm_mci_init_flow.m b/toolbox/mci/inference/spm_mci_init_flow.m new file mode 100644 index 00000000..a54cabc0 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_init_flow.m @@ -0,0 +1,38 @@ +function [x_init,x_flow] = spm_mci_init_flow (assign,w,v,M) +% Extract init, flow and out params from rfx and ffx vectors +% FORMAT [x_init,x_flow] = spm_mci_init_flow (assign,w,v,M) +% +% assign fields specify which are random/fixed effects +% w random effects vector +% v fixed effects vector +% M model structure +% +% x_init init params +% x_flow flow params (includes out params) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_init_flow.m 6548 2015-09-11 12:39:47Z will $ + +switch assign.init_par, + case 'known', + x_init=M.x0; + case 'random' + x_init=w(assign.w_init); + otherwise + % Assume fixed effect + x_init=v(assign.v_init); +end + +if strcmp(assign.flow_par,'random') + x_flow=w(assign.w_flow); +else + x_flow=v(assign.v_flow); +end + +if strcmp(assign.out_par,'random') + x_flow=[x_flow;w(assign.w_out)]; +else + x_flow=[x_flow;v(assign.v_out)]; +end diff --git a/toolbox/mci/inference/spm_mci_isvl.m b/toolbox/mci/inference/spm_mci_isvl.m new file mode 100644 index 00000000..bb0d4a4d --- /dev/null +++ b/toolbox/mci/inference/spm_mci_isvl.m @@ -0,0 +1,93 @@ +function [isvl] = spm_mci_isvl (mcmc,M,U,Y,VL) +% Compute Log Evidence using Importance Sampling +% FORMAT [isvl] = spm_mci_isvl (mcmc,M,U,Y,VL) +% +% mcmc Optimisation parameters eg. +% +% .maxits number of samples to use +% +% M Model structure +% U Input structure +% Y Data +% +% isvl +% .logev log evidence +% .L(s) log likelihood of sth sample +% .v(s) importance weight of sth sample +% .logev_est(S) estimate based on first S samples only +% .logev_boot(b) estimate based on bth bootstrap resample (of size .maxits) +% +% Uses IS with VL posterior as proposal +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_isvl.m 6548 2015-09-11 12:39:47Z will $ + +M = spm_mci_minit (M); +V = M.V; +M.vpE=spm_vec(M.pE); + +Ep=VL.Ep; +Cp=full(VL.Cp); + +VL.post0=-0.5*spm_logdet(Cp); +VL.prior0=-0.5*spm_logdet(M.pC); +VL.iCp=inv(Cp); +VL.ipC=inv(M.pC); + +% Generate S samples from prior +S=mcmc.maxits; +w=spm_normrnd(Ep,Cp,S); + +parfor s=1:S, + [L(s),v(s)] = spm_mci_isvl_single (M,U,Y,VL,w(:,s)); +end + +for s=1:S, + logev_est(s) = isvl_evidence (L(1:s),v(1:s)); +end + +% Estimate based on all samples +logev=logev_est(S); + +% Normalised importance weights +u = v/sum(v); + +% Get logev for bootstrap resamples +Nboot=1000; +for b=1:Nboot, + ind=ceil(rand(1,S)*S); + L_resample=L(ind); + v_resample=v(ind); + logev_boot(b)=isvl_evidence(L_resample,v_resample); +end + +% Outputs +isvl.logev = logev; +isvl.L = L; +isvl.v = v; +isvl.logev_est = logev_est; +isvl.logev_boot = logev_boot; + +end + +%------------------------------------------------------ + +function [logev] = isvl_evidence (L,v) + +% Normalised importance weights +u = v/sum(v); + +% Compute log evidence being careful to avoid overflow +lu = log(u); +lup = lu + L; +lupmax = max (lup); +r = exp(lup - lupmax); +logev = log (sum(r))+lupmax; + +end + + + + diff --git a/toolbox/mci/inference/spm_mci_lgv.m b/toolbox/mci/inference/spm_mci_lgv.m new file mode 100644 index 00000000..3e2f2371 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_lgv.m @@ -0,0 +1,186 @@ +function [M,stats] = spm_mci_lgv (mcmc,M,U,Y) +% Sampling using Langevin Monte Carlo +% FORMAT [M,stats] = spm_mci_lgv (mcmc,M,U,Y) +% +% mcmc Sampling parameters +% .verbose display progress +% .maxits maximum number of total samples +% .init initial sample values (start of chain) +% .update_obs_noise estimate observation noise +% .update_obs_step update obs noise after this number of samples +% .restart restart chain from init +% .h step size +% .adapt_h adapt h based on acceptance rate +% +% M Model Structure +% .dL Gradients and curvatures are computed using +% this user-specified function. If this is absent +% they will be computed using (i) the forward +% sensitivity method for dynamical models +% (ie. if M.f exists) or (ii) finite differences +% otherwise +% +% U Inputs +% Y Data +% +% M Updated model structure +% stats Structure with fields: +% +% .P Samples, [maxits x M.Np] +% .E Negative log joint prob, [maxits x 1] +% +% Uses Simplified Manifold Metropolis Adjusted Langevin +% Algorithm (Simplified MMALA). +% +% The manifold matrix captures local curvature but local changes +% in it are ignored [1,2]. The manifold matrix is more simply +% interpreted as the posterior covariance under local linear +% assumptions. +% +% [1] Calderhead and Girolami. Interface Focus (2011), pp 821-835. +% [2] Girolami and Calderhead. J R Stat Soc B (2011), pp 123-214. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_lgv.m 6548 2015-09-11 12:39:47Z will $ + +% Defaults +try verbose=mcmc.verbose; catch verbose=0; end +try maxits=mcmc.maxits; catch maxits=64; end +try restart=mcmc.restart; catch restart=0; end +try plot_int=mcmc.plot_int; catch plot_int=1; end +try update_obs_noise=mcmc.update_obs_noise; catch update_obs_noise=1; end +try update_obs_step=mcmc.update_obs_step; catch update_obs_step=maxits/2; end +try h=mcmc.h; catch h=0.5; end +try adapt_h=mcmc.adapt_h; catch adapt_h=0; end +try init=mcmc.init; catch init=spm_vec(M.pE); end + +% Observation noise +Ny=size(Y,2); +noise.c0=Ny; +noise.D0=eye(Ny); + +% Compute eigen-parameterisation +M = spm_mci_minit (M); +V = M.V; + +% Initial param in eigenspace +xinit = M.V'*(init-M.vpE); + +% Sample matrix +x = zeros(maxits,M.Np); +x(1,:) = xinit'; + +if verbose figure; end + +% Parameters for adapting h by monitoring acceptance rate +acc_block=64; +acc_low=0.3; +acc_high=0.7; + +acc=zeros(maxits,1); + +if ~isfield(M,'Ce') + % Don't update observation noise if we don't have a + % Gaussian noise model + update_obs_noise=0; +end + +% Main Loop +i=1; Ce=[]; +while (i <= maxits), + + if verbose + if mod(i,plot_int) == 0 & i > 2 + spm_mci_progress (x,E,i); + end + end + + if mod(i,acc_block)==0 & adapt_h + % Change step size h ? + Nacc=sum(acc(i-acc_block+1:i-1)); + prop_acc=Nacc/acc_block; + if prop_acc < acc_low + if verbose, disp('Decreasing step size ...'); end + h=h/2; + elseif prop_acc > acc_high + if verbose, disp('Increasing step size ...'); end + h=h*2; + end + end + + % Proposal (first proposal always accepted) + if i==1 + pos=x(1,:)'; + curr=[]; + else + pos=spm_normrnd(curr.mu,curr.Cp,1); + end + + % Quantities re proposal + prop.pos=pos; + [j,iCpY,st,prop.L,prop.L2] = spm_mci_joint_grad (pos,M,U,Y); + if st==-1 + disp('Integration problem in spm_mci_lgv.m'); + keyboard + end + % Posterior covariance under local linear approximation + prop.Cp = h*inv(iCpY+M.ipC); + prop.mu = pos+0.5*h*prop.Cp*j(:); + + prop.iCp = inv(prop.Cp); + prop.logdetCp = spm_logdet(prop.Cp); + + % Accept proposal ? + [curr,accepted,bayes_fb(i),dL(i)] = spm_mci_mh_update(curr,prop,verbose); + acc(i)=accepted; + E(i) = -curr.L; + L2(i) = curr.L2; + if i > 1 + dEdit(i-1)=100*(E(i)-E(i-1))/E(i-1); + end + x(i,:)=curr.pos; + + % Update observation noise + if (i > update_obs_step & update_obs_noise) | (restart & update_obs_noise) + if verbose, disp('Updating observation noise'); end + try ind=Y.ind; catch ind=1:M.N; end + % Parameters in original space + xorg = M.V*x(i,:)'+M.vpE; + if isfield(M,'IS') + %yhat = feval(M.IS,x(i,:)',M,U); + yhat = feval(M.IS,xorg,M,U); + err=Y-yhat; + else + %yhat = spm_mci_fwd (x(i,:)',M,U); + yhat = spm_mci_fwd (xorg,M,U); + err=Y-yhat(ind,:); + end + NT=size(err,1); + noise.cN=noise.c0+0.5*NT; + noise.DN=noise.D0+0.5*NT*cov(err); + Lprec=spm_wishrnd(noise.DN,noise.cN); + M.Ce=inv(Lprec); + %M.Ce=iwishrnd(noise.DN,noise.cN); + Ce(:,:,i-update_obs_step)=M.Ce; + end + + i=i+1; +end + +if verbose + disp(sprintf('Total accepted samples = %d', sum(acc))); +end +% Project parameters back from eigenspace into original space +x=x(1:i-1,:); +nj=size(x,1); +stats.P=M.vpE*ones(1,nj)+V*x'; + +stats.E=E; +stats.dEdit=dEdit; +stats.acc=acc; +stats.Ce=Ce; +stats.bayes_fb=bayes_fb; +stats.dL=dL; +stats.L2=L2; diff --git a/toolbox/mci/inference/spm_mci_lgv_vl.m b/toolbox/mci/inference/spm_mci_lgv_vl.m new file mode 100644 index 00000000..c6d090b5 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_lgv_vl.m @@ -0,0 +1,131 @@ +function [M,stats] = spm_mci_lgv_vl (mcmc,M,U,Y,vl,beta) +% Sampling using Langevin Monte Carlo on path from VL solution +% FORMAT [M,stats] = spm_mci_lgv_vl (mcmc,M,U,Y,vl,beta) +% +% mcmc Sampling parameters +% .verbose display progress +% .maxits maximum number of total samples +% .init initial sample values (start of chain) +% .h step size +% +% M Model Structure +% .dL Gradients and curvatures are computed using +% this user-specified function. If this is absent +% they will be computed using (i) the forward +% sensitivity method for dynamical models +% (ie. if M.f exists) or (ii) finite differences +% otherwise +% +% U Inputs +% Y Data +% vl Variational Laplace solution +% .Ep Posterior Mean +% .Cp Posterior Covariance +% beta Inverse Temperature (0 at VL solution, 1 at posterior) +% +% M Updated model structure +% stats Structure with fields: +% +% .P Samples, [maxits x M.Np] +% .E Negative log joint prob, [maxits x 1] +% +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_lgv_vl.m 6548 2015-09-11 12:39:47Z will $ + +% Defaults +try verbose=mcmc.verbose; catch verbose=0; end +try maxits=mcmc.maxits; catch maxits=64; end +try h=mcmc.h; catch h=0.5; end +try init=mcmc.init; catch init=spm_vec(M.pE); end + +% Compute eigen-parameterisation +M = spm_mci_minit (M); +V = M.V; + +% Initial param in eigenspace +xinit = M.V'*(init-M.vpE); +Nr = length(xinit); + +% Sample matrix +x = zeros(maxits,M.Np); +x(1,:) = xinit'; + +if verbose figure; end + +acc=zeros(maxits,1); + +% Main Loop +i=1; Ce=[]; +while (i <= maxits), + + if verbose + if mod(i,plot_int) == 0 & i > 2 + spm_mci_progress (x,E,i); + end + end + + % Proposal (first proposal always accepted) + if i==1 + pos=x(1,:)'; + curr=[]; + else + pos=spm_normrnd(curr.mu,curr.Cp,1); + end + + % Quantities re proposal + prop.pos=pos; + [j,iCpY,st,prop.L,prop.L2] = spm_mci_joint_grad (pos,M,U,Y); + if st==-1 + disp('Integration problem in spm_mci_lgv.m'); + keyboard + end + + % Compute gradient for this temperature + pos_orgspace=V*pos+M.vpE; + j2 = (1-beta)*vl.Lambdap*(vl.Ep-pos_orgspace); + %j2 = (1-beta)*vl.Lambdap*(pos_orgspace-vl.Ep); + j = beta*j + j2'; + + % Covariance for this temperature + %prop.Cp = (h/beta)*eye(Nr); + %prop.Cp = (h/beta)*inv(iCpY+M.ipC); + prop.Cp = h*inv((1-beta)*vl.Lambdap+beta*iCpY+beta*M.ipC); + + % Posterior mean under local linear approximation + prop.mu = pos+0.5*h*prop.Cp*j(:); + + prop.iCp = inv(prop.Cp); + prop.logdetCp = spm_logdet(prop.Cp); + + % Accept proposal ? + [curr,accepted,bayes_fb(i),dL(i)] = spm_mci_mh_update(curr,prop,verbose); + acc(i)=accepted; + E(i) = -curr.L; + L2(i) = curr.L2; + if i > 1 + dEdit(i-1)=100*(E(i)-E(i-1))/E(i-1); + end + x(i,:)=curr.pos; + + i=i+1; +end + +if verbose + disp(sprintf('Total accepted samples = %d', sum(acc))); +end + +% Project parameters back from eigenspace into original space +x=x(1:i-1,:); +nj=size(x,1); +stats.P=M.vpE*ones(1,nj)+V*x'; + +stats.E=E; +stats.dEdit=dEdit; +stats.acc=acc; +stats.Ce=Ce; +stats.bayes_fb=bayes_fb; +stats.dL=dL; +stats.L2=L2; diff --git a/toolbox/mci/inference/spm_mci_like_ind.m b/toolbox/mci/inference/spm_mci_like_ind.m new file mode 100644 index 00000000..1d5dfd50 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_like_ind.m @@ -0,0 +1,34 @@ +function [L,e] = spm_mci_like_ind (P,R,M,U,Y) +% Compute likelihood wrt selected time points +% FORMAT [L,e] = spm_mci_like_ind (P,R,M,U,Y) +% +% P Flow parameters +% R Initial state parameters +% M Model structure +% U Inputs [Nin x N] +% Y data +% +% L Log likelihood +% e Prediction errors +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: spm_mci_like_ind.m 6548 2015-09-11 12:39:47Z will $ + +% Read data points and time indices +try ind=Y.ind; catch ind=1:M.N; end +Nt=length(ind); +y=Y.y; + +M.x0=R; % Initial conditions +[G,sy,st] = spm_mci_fwd (P,M,U); + +if st==-1, disp('Problem !'); return; end + +% Prediction errors +g=G(ind,:); +e=Y.y-g; + +% Log Likelihood +L = -0.5*trace(M.iCe*e'*e) + M.logdet_Ce - 0.5*Nt*log(2*pi); \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_mfx.m b/toolbox/mci/inference/spm_mci_mfx.m new file mode 100644 index 00000000..61a8b3b0 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_mfx.m @@ -0,0 +1,206 @@ +function [MCI] = spm_mci_mfx (MCI) +% Mixed Effects Inference +% FORMAT [MCI] = spm_mci_mfx (MCI) +% +% MCI Data structure containing fields: +% +% .M{n} Model for nth of N replications (e.g. subjects) +% .U{n} Inputs for nth replication +% .Y{n} Data for nth replication +% .S Second level model describing population mean, m, and +% precision, Lambda. The parameters in S.prior +% define the sufficient statistics of p(Lambda) (.a and .B) +% and p(m|Lambda) (.beta and.m) +% +% .inference 'amc' or 'lgv' (default) +% .total_its Total number of samples per subject +% .rinit Proportion of samples to collect prior to use of +% Empirical (group) prior +% .verbose Show progress of optimisation +% .update_obs_noise Update observation noise ? [yes/no] (1/0), default=1 +% +% The output fields are: +% +% POSTERIOR SAMPLES: +% .sm [Nw x Nsamples] group random effect means, m +% .sw [Nw x N x Nsamples] subject random effects, w +% .Ce [Ny x Ny x Nsamples] Obs noise covariance samples +% .postind Indices for posterior (ie. excluding burn-in) +% +% POSTERIOR MEANS: +% .sm_mean [Nw x 1] posterior mean over m +% .sw_mean [Nw x N] posterior mean over w +% +% SUFFICIENT STATISTICS: +% .noise Parameters of p(Gamma|Y,w,v): .c0,.D0,.cN,.DN +% .S.post Parameters of p(Lambda|w) (.a and.B) +% and p(m|Lambda,w) (.beta and .m) +% +% W.Penny, M Klein-Flugge and B Sengupta (2015) Mixed Effects Langevin +% Monte Carlo, Submitted, 2015. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_mfx.m 6548 2015-09-11 12:39:47Z will $ + +try verbose=MCI.verbose; catch verbose=0; end +try inference=MCI.inference; catch inference='lgv'; end + +% Update observation noise ? +try update_obs_noise=MCI.update_obs_noise; catch update_obs_noise=1; end + +try total_its=MCI.total_its; catch total_its=1024; end +try rinit=MCI.rinit; catch rinit=0.25; end + +rfx_its=2; +mfx_its=ceil((1-rinit)*total_its); +rfx1_its=total_its-mfx_its; + +M=MCI.M;U=MCI.U;Y=MCI.Y; +Np=length(spm_vec(M{1}.pE)); + +% Observation noise +Ny=size(Y{1}.y,2); +noise.c0=Ny; +noise.D0=eye(Ny); + +% Prior over random effects (second level model) +try + S=MCI.S; +catch + S.prior.P=Np; + S.prior.a=Np/2; + S.prior.B=eye(Np); + S.prior.beta=1; + S.prior.m=zeros(Np,1); + S.N=length(M); +end +% Prior mean and cov of random effects from second level model +m=S.prior.m; +C=S.prior.B/S.prior.a; +rfx_prior.pE=m; +rfx_prior.pC=C; +MCI.S=S; + +% Initialisation of each model +subj_mcmc.verbose=verbose; +subj_mcmc.maxits=rfx1_its; +for n=1:S.N, + subj_mcmc.init=M{n}.pE; + if rfx1_its > 0 + switch inference, + case 'lgv', + Psamp = spm_mci_random (subj_mcmc,rfx_prior,[],M{n},U{n},Y{n}); + + case 'amc', + M{n}.pE=rfx_prior.pE; + M{n}.pC=rfx_prior.pC; + Nscale=0; + Nsamp=ceil(0.5*rfx1_its); + Ntune=Nsamp; + amc = spm_mci_popdef (Nscale,Ntune,Nsamp); + amc.verbose=verbose; + Mc{1}=M{n};Uc{1}=U{n}; + Ptmp=spm_mci_pop (amc,Mc,Uc,Y{n}); + Psamp=Ptmp{1}.theta; + + otherwise + disp('Unknown inference method in spm_mci_mfx.m'); + end + sw(n,:,:) = Psamp; + w(:,n) = Psamp(:,end); + else + sw(n,:,1) = M{n}.pE; + w(:,n) = M{n}.pE; + end +end +sw=permute(sw,[2 1 3]); +sm=squeeze(mean(sw,2)); + +if isfield(M{1},'Ce') + gauss_noise=1; + Ce(:,:,1:rfx1_its)=M{1}.Ce; +else + gauss_noise=0; +end +subj_mcmc.maxits=rfx_its; + + +% Main Loop +for it=1:mfx_its, + + if verbose + disp(sprintf('RFX iteration %d',it)); + end + + if it>1 + % Update second level params + S = spm_nwpost (S,w); + [m,Lambda,C] = spm_nwrnd (S.post,1); + end + + % Updated prior on random effects + rfx_prior.pE=m; + rfx_prior.pC=C; + + % 1. Update estimates of subject random effects + if verbose, disp('Updating random effects'); end + + for n=1:S.N, + % Start sampling where left off + subj_mcmc.init=w(:,n); + + switch inference, + case 'lgv', + Psamp = spm_mci_random (subj_mcmc,rfx_prior,[],M{n},U{n},Y{n}); + + case 'amc', + M{n}.pE=rfx_prior.pE; + M{n}.pC=rfx_prior.pC; + Nscale=0; Ntune=0; Nsamp=rfx_its; + amc = spm_mci_popdef (Nscale,Ntune,Nsamp); + amc.verbose=verbose; + Mc{1}=M{n};Uc{1}=U{n}; + Ptmp=spm_mci_pop (amc,Mc,Uc,Y{n}); + Psamp=Ptmp{1}.theta; + + otherwise + disp('Unknown inference method in spm_mci_mfx.m'); + end + w(:,n) = Psamp(:,end); + end + + % Update observation noise precision + if update_obs_noise & gauss_noise + if verbose, disp('Updating observation noise precision'); end + [noise,M] = spm_mci_obsnoise (w,[],[],noise,M,U,Y); + end + if gauss_noise, Ce(:,:,rfx1_its+it)=M{1}.Ce; end + + % Store samples + sm(:,rfx1_its+it)=m; + sw(:,:,rfx1_its+it)=w; + +end + +% Define post burn-in +total_its=size(sm,2); +burn_in=round(0.3*total_its); +post_ind=[burn_in+1:total_its]; + +% Return samples +MCI.sm=sm; +MCI.sw=sw; + +% Return posterior means +MCI.sm_mean=mean(sm(:,post_ind),2); +MCI.sw_mean=mean(sw(:,:,post_ind),3); +MCI.post_ind=post_ind; + +MCI.M=M; +MCI.S=S; +if gauss_noise + MCI.noise=noise; + MCI.Ce=Ce; +end diff --git a/toolbox/mci/inference/spm_mci_mfx_dynamic.m b/toolbox/mci/inference/spm_mci_mfx_dynamic.m new file mode 100644 index 00000000..6e1243c8 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_mfx_dynamic.m @@ -0,0 +1,296 @@ +function [MCI] = spm_mci_mfx_dynamic (MCI) +% Mixed Effects Inference for Dynamical Systems +% FORMAT [MCI] = spm_mci_mfx_dynamic (MCI) +% +% MCI Data structure containing fields: +% +% .M{n} Model for nth of N replications (e.g. subjects) +% .U{n} Inputs for nth replication +% .Y{n} Data for nth replication +% .fixed Gaussian prior (.pE and .pC) over FFX +% .S Second level model describing population mean, m, and +% precision, Lambda. The parameters in S.prior +% define the sufficient statistics of p(Lambda) (.a and .B) +% and p(m|Lambda) (.beta and.m) +% .total_its Total number of samples per subject +% .rinit Proportion of samples to collect prior to use of +% Empirical (group) prior +% .verbose Show progress of optimisation +% +% KNOWN, FIXED or RANDOM EFFECTS: +% The initial states, flow and output +% parameters can be fixed or random effects or take on known values: +% +% .assign.init_par 'fixed', 'random' or 'known' +% .assign.flow_par 'fixed', 'random' or 'known' +% .assign.out_par 'fixed', 'random' or 'known' +% +% .pinit0 Initial values of initial state parameters +% [Ninit x 1] for fixed, [Ninit x N] for random +% .pflow0 Initial values of flow parameters +% [Nflow x 1] for fixed, [Nflow x N] for random +% .pout0 Initial values of output parameters +% [Nout x 1] for fixed, [Nout x N] for random +% +% .update_obs_noise Update observation noise, Gamma ? [yes/no] (1/0), default=1 +% .verbose Show progress of optimisation +% +% The output fields are: +% +% POSTERIOR SAMPLES: +% .sv [Nv x Nsamples] group fixed effects samples, v +% .sm [Nw x Nsamples] group random effect means, m +% .sw [Nw x N x Nsamples] subject random effects, w +% .Ce [Ny x Ny x Nsamples] Obs noise covariance samples +% .postind Indices for posterior (ie. excluding burn-in) +% +% POSTERIOR MEANS: +% .sv_mean [Nv x 1] posterior mean over v +% .sm_mean [Nw x 1] posterior mean over m +% .sw_mean [Nw x N] posterior mean over w +% +% For Dynamical Systems models: +% .pinit Estimated initial states +% .pflow Estimated flow +% .pout Estimated output params +% +% SUFFICIENT STATISTICS: +% .noise Parameters of p(Gamma|Y,w,v): .c0,.D0,.cN,.DN +% .S.post Parameters of p(Lambda|w) (.a and.B) +% and p(m|Lambda,w) (.beta and .m) +% +% W.Penny, M Klein-Flugge and B Sengupta (2015) Mixed Effects Langevin +% Monte Carlo, Submitted, 2015. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_mfx_dynamic.m 6548 2015-09-11 12:39:47Z will $ + +try verbose=MCI.verbose; catch verbose=0; end + +% Update observation noise ? +try update_obs_noise=MCI.update_obs_noise; catch update_obs_noise=1; end + +% Numbers of iterations, with and without group model +try total_its=MCI.total_its; catch total_its=1024; end +try rinit=MCI.rinit; catch rinit=0.25; end +rfx_its=2; +mfx_its=(1-rinit)*total_its/rfx_its; +rfx1_its=rinit*total_its; + +try ffx_its=MCI.ffx_its; catch ffx_its=4; end +try ffx1_its=MCI.ffx1_its; catch ffx1_its=64; end + +M=MCI.M;U=MCI.U;Y=MCI.Y; +Np=length(spm_vec(M{1}.pE)); +if isfield(M{1},'x0') + % For dynamical systems + d=size(M{1}.x0,1); +end + +% Observation noise +Ny=size(Y{1}.y,2); +noise.c0=Ny; +noise.D0=eye(Ny); + +% Prior over fixed effects +try + fixed=MCI.fixed; + fixed.vpE=spm_vec(fixed.pE); % m_v (vector) +catch + fixed=[]; +end + +% Prior over random effects (second level model) +try + S=MCI.S; + Nrand=S.prior.a; +catch + if strcmp(MCI.assign.init_par,'random') + Nrand=d; + else + Nrand=0; + end + if strcmp(MCI.assign.flow_par,'random') + Nrand=Nrand+M{1}.Npflow; + end + if strcmp(MCI.assign.out_par,'random') + Nrand=Nrand+M{1}.Npout; + end + S.prior.P=Nrand; + S.prior.a=Nrand/2; + S.prior.B=eye(Nrand); + S.prior.beta=1; + S.prior.m=zeros(Nrand,1); + S.N=length(M); +end +% Prior mean and cov of random effects from second level model +m=S.prior.m; +C=S.prior.B/S.prior.a; +rfx_prior.pE=m; +rfx_prior.pC=C; +MCI.S=S; + +% Initialise fixed and random effects +[w_init,v_init,assign,update_ffx,update_rfx] = spm_mci_vw_init (MCI); + +% Sampling params +fixed_mcmc.assign=assign; +subj_mcmc.assign=assign; +subj_mcmc.verbose=0; + +% Set up sample matrix +Nfixed = length(v_init); +if Nfixed > 0 + %v = zeros(mfx_its,Nfixed); + for j=1:rfx1_its, + v(j,:) = v_init'; + end +end +w = w_init; + +% Initialisation of each model +subj_mcmc.verbose=verbose; +subj_mcmc.maxits=rfx1_its; +for n=1:S.N, + subj_mcmc.init=w(:,n); + if rfx1_its > 0 + Psamp = spm_mci_random (subj_mcmc,rfx_prior,v_init,M{n},U{n},Y{n}); + sw(n,:,:) = Psamp; + w(:,n) = Psamp(:,end); + else + sw(n,:,1) = w(:,n); + end +end +sw=permute(sw,[2 1 3]); +sm=squeeze(mean(sw,2)); +for j=1:rfx1_its, + Ce(:,:,j)=M{1}.Ce; +end +subj_mcmc.maxits=rfx_its; +subj_mcmc.verbose=0; + +% Main Loop +for it=1:mfx_its, + + if verbose + disp(sprintf('MCI iteration %d',it)); + end + + if it>1 & Nrand > 0 + % Update second level params + S = spm_nwpost (S,w); + [m,Lambda,C] = spm_nwrnd (S.post,1); + end + + + if update_ffx + if verbose, disp('Updating fixed effects'); end + % 2. Update estimate of group fixed effects + + % Start sampling where left off + fixed_mcmc.init=v(it,:)'; + fixed_mcmc.update_obs_noise=update_obs_noise; + + if it==1 + fixed_mcmc.verbose=1; + fixed_mcmc.maxits=ffx1_its; + else + fixed_mcmc.maxits=ffx_its; + fixed_mcmc.verbose=0; + end + [Psamp,noise,M] = spm_mci_fixed (fixed_mcmc,w,fixed,noise,M,U,Y); + P = Psamp(end,:); + v(rfx1_its+it,:) = P; + vit=P'; + else + vit=[]; + end + + if update_rfx + % Updated prior on random effects + rfx_prior.pE=m; + rfx_prior.pC=C; + + % 1. Update estimates of subject random effects + if verbose, disp('Updating random effects'); end + + for n=1:S.N, + if verbose + disp(sprintf('Subject %d out of %d',n,S.N)); + end + % Start sampling where left off + subj_mcmc.init=w(:,n); + Psamp = spm_mci_random (subj_mcmc,rfx_prior,vit,M{n},U{n},Y{n}); + w(:,n) = Psamp(:,end); + end + + end + + % Update observation noise precision + if update_obs_noise + if verbose, disp('Updating observation noise precision'); end + [noise,M] = spm_mci_obsnoise (w,vit,assign,noise,M,U,Y); + end + Ce(:,:,rfx1_its+it)=M{1}.Ce; + + % Store samples + sm(:,rfx1_its+it)=m; + sw(:,:,rfx1_its+it)=w; + +end + +% Define post burn-in +total_its=size(sm,2); +burn_in=round(0.3*total_its); +post_ind=[burn_in+1:total_its]; + +% Return samples +MCI.sm=sm; +MCI.sw=sw; +try MCI.sv=v'; catch MCI.sv=[]; end + +% Return posterior means +if update_rfx + MCI.sm_mean=mean(sm(:,post_ind),2); + MCI.sw_mean=mean(sw(:,:,post_ind),3); +end +try MCI.sv_mean=mean(v(post_ind,:)); catch MCI.sv_mean=[]; end +MCI.post_ind=post_ind; + +% Extract posterior means for pinit, pflow, pout +switch assign.init_par, + case 'fixed', + MCI.pinit=MCI.sv_mean(:,assign.v_init)'; + case 'random', + MCI.pinit_sub=MCI.sw_mean(assign.w_init,:); + MCI.pinit=MCI.sm_mean(assign.w_init,:); + otherwise + try MCI.pinit=MCI.pinit0; catch MCI.pinit=[]; end +end +switch assign.flow_par, + case 'fixed', + MCI.pflow=MCI.sv_mean(:,assign.v_flow)'; + case 'random', + MCI.pflow_sub=MCI.sw_mean(assign.w_flow,:); + MCI.pflow=MCI.sm_mean(assign.w_flow,:); + otherwise + try MCI.pflow=MCI.flow0; catch MCI.pflow=[]; end +end +switch assign.out_par, + case 'fixed', + MCI.pout=MCI.sv_mean(:,assign.v_out)'; + case 'random', + MCI.pout_sub=MCI.sw_mean(assign.w_out,:); + MCI.pout=MCI.sm_mean(assign.w_out,:); + otherwise + try MCI.pout=MCI.out0; catch MCI.pout=[]; end +end + +MCI.M=M; +MCI.S=S; +MCI.noise=noise; +MCI.assign=assign; +MCI.Ce=Ce; + diff --git a/toolbox/mci/inference/spm_mci_mh.m b/toolbox/mci/inference/spm_mci_mh.m new file mode 100644 index 00000000..0b2cfec0 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_mh.m @@ -0,0 +1,68 @@ +function [P,L,D] = spm_mci_mh (mcmc,M,U,Y) +% Metropolis Hastings with Gaussian priors and proposals +% FORMAT [P,L,D] = spm_mci_mh (mcmc,M,U,Y) +% +% mcmc Optimisation parameters eg. +% +% .nsamp number of samples to return +% .Cprop proposal density +% .init initial parameter point +% +% M Model structure +% U Inputs +% Y Data +% +% P Posterior samples +% L Logjoint history +% D Diagnostics (D.accept_rate, D.els) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_mh.m 6548 2015-09-11 12:39:47Z will $ + +% Compute eigen-parameterisation +M = spm_mci_minit (M); +V = M.V; + +try init=mcmc.init; catch init=M.vpE; end + +% Initialise proposal covariance +Cprop=V'*mcmc.Cprop*V; +Nr=size(Cprop,1); + +% perturbations for proposals +delta_theta=spm_normrnd(zeros(Nr,1),Cprop,mcmc.nsamp); + +% Initial param in eigenspace +theta = M.V'*(init-M.vpE); +L = spm_mci_joint (theta,M,U,Y); + +tic; +accept=1; +for i=2:mcmc.nsamp, + + xcand=theta(:,end)+delta_theta(:,i); + Lnew = spm_mci_joint (xcand,M,U,Y); + + % Accept candidate ? + accept_prob=min(1,exp(Lnew-L(end))); + if rand(1) < accept_prob, + theta=[theta xcand]; + L(end+1)=Lnew; + accept=accept+1; + else + theta(:,end+1)=theta(:,end); + L(end+1)=L(end); + end + +end + +% Project parameters from eigenspace back into original space +nj=size(theta,2); +P=M.vpE*ones(1,nj)+V*theta; + +D.accept_rate=accept/mcmc.nsamp; +D.els=toc; + + diff --git a/toolbox/mci/inference/spm_mci_mh_update.m b/toolbox/mci/inference/spm_mci_mh_update.m new file mode 100644 index 00000000..cddd048a --- /dev/null +++ b/toolbox/mci/inference/spm_mci_mh_update.m @@ -0,0 +1,52 @@ +function [next,accepted,bayes_fb,dL] = spm_mci_mh_update (curr,prop,verbose) +% Update parameters using Metropolis-Hastings +% FORMAT [next,accepted,bayes_fb,dL] = spm_mci_mh_update (curr,prop,verbose) +% +% curr quantities re current state +% prop quantities re proposed state +% verbose 1 for text output +% +% next next state +% accepted 1 for accepted proposal +% bayes_fb Log Bayes factor for forward versus backward transition +% dL Proposed difference in log joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_mh_update.m 6548 2015-09-11 12:39:47Z will $ + +accepted=1; + +if isempty(curr) + % must be first move - accept proposal + next = prop; + bayes_fb = 0; + dL = 0; +else + % Evaluate transition probabilities forward and backward + ef = prop.pos-curr.mu; + Lforward = -0.5*curr.logdetCp - 0.5*ef'*curr.iCp*ef; + eb = curr.pos-prop.mu; + Lback = -0.5*prop.logdetCp - 0.5*eb'*prop.iCp*eb; + bayes_fb = Lforward - Lback; + dL = prop.L - curr.L; + + Ltot = prop.L - curr.L + Lback - Lforward; + r = exp(Ltot); + alpha = min(1,r); + test_prob = rand(1); + + % Hastings ratio + if alpha > test_prob + if verbose + display(['*********** sample accepted *****************']); + end + % accept and update parameters + next = prop; + else + % reject move + next = curr; + accepted = 0; + end +end \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_minit.m b/toolbox/mci/inference/spm_mci_minit.m new file mode 100644 index 00000000..3a2446c3 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_minit.m @@ -0,0 +1,26 @@ +function [M] = spm_mci_minit (M) +% Check and initialise model strucuture +% FORMAT [M] = spm_mci_minit (M) +% +% eg. Pre-compute quantities for computing log-joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_minit.m 6548 2015-09-11 12:39:47Z will $ + +if isstruct(M.pE) + M.vpE=spm_vec(M.pE); +else + M.vpE=M.pE; +end +if isfield(M,'Ce') + M.logdet_Ce=spm_logdet(M.Ce); + try + M.iCe=M.iCe; + catch + M.iCe = inv(M.Ce); + end +end + +M = spm_mci_priors (M); \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_mvnpost.m b/toolbox/mci/inference/spm_mci_mvnpost.m new file mode 100644 index 00000000..5bcb83a7 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_mvnpost.m @@ -0,0 +1,72 @@ +function [stats,Y,X] = spm_mci_mvnpost (post,method,verbose,max_lag) +% Are MCMC samples consistent with Gaussian posterior ? +% FORMAT [stats,Y,X] = spm_mci_mvnpost (post,method,verbose,max_lag) +% +% post posterior data structure from spm_mci_post +% method 'ESS' or 'thinning' +% verbose create plots +% max_lag maximum potential lag for MAR model +% +% stats (multivariate) normal test statistics +% See spm_mci_mvntest.m +% Y uncorrelated posterior samples +% X original posterior samples +% +% Run Gaussianity test on Markov chain samples +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_mvnpost.m 6548 2015-09-11 12:39:47Z will $ + +try pmax=max_lag; catch pmax=10; end +try meth=method; catch meth='ESS'; end +try ver=verbose; catch ver=1; end + + +j=post.ind; +X=post.P(:,j)'; +Nj=length(j); +Np=size(post.P,1); + +if any(std(X)==0) + disp('Warning from spm_mci_mvnpost: some parameters have zero variance'); + stats=[]; + return +end + +switch meth + case 'ESS', + %C=cov(post.P(:,post.ind)'); + for p=1:Np, + ess(p)=spm_mci_ess(post.P(p,j)); + %v(p)=C(p,p)*ess(p)/Nj; + end + %post.Cp=diag(v); + %mess=mean(ess); + mess=ceil(min(ess)); + stats = spm_mci_mvntest(X,mess); + + if ver + disp('ESS method'); + disp('Effective Sample Sizes:'); + disp(ess); + end + + case 'thinning', + for p=1:Np, + [tmp,m(p)]=spm_mci_ess(post.P(p,j)); + end + lag=max(m)+1; + Y=X(1:lag:end,:); + stats = spm_mci_mvntest(Y); + if ver + disp('Thinning method'); + disp(sprintf('Using only every %d-th sample',lag)); + end + + + otherwise + disp('Unknown method in spm_mci_mvnpost.m'); + return +end \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_mvntest.m b/toolbox/mci/inference/spm_mci_mvntest.m new file mode 100644 index 00000000..a7b2746e --- /dev/null +++ b/toolbox/mci/inference/spm_mci_mvntest.m @@ -0,0 +1,173 @@ +function [stats] = spm_mci_mvntest (X,df) +% Test for multivariate normality +% FORMAT [stats] = spm_mci_mvntest (X,df) +% +% X [N x d] data matrix containing N IID samples of d-variate data +% df Degrees of freedom e.g. df <= N +% +% stats +% .p p-value for multivariate normality +% e.g. with p < 0.05 one can reject null hypothesis of normality +% .W(j) Shapiro-Wilks statistic for jth variate +% .Z(j) Equivalent standardised Gaussian variate for W(j) +% .pusw(j) p-value for normality of jth variate (Shapiro-Wilks) +% .puks(k) p-value for normality of jth variate (Kolmogorov-Smirnoff) +% .k kurtosis of jth variate +% .s skewness of jth variate +% +% This function is adapted from the matlab function Roystest.m: +% +% Trujillo-Ortiz, A., R. Hernandez-Walls, K. Barba-Rojo and +% L. Cupul-Magana. (2007). Roystest:Royston's Multivariate Normality Test. +% A MATLAB file. [WWW document]. URL http://www.mathworks.com/ +% matlabcentral/fileexchange/loadFile.do?objectId=17811 +% +% Royston, J.P. (1992). Approximating the Shapiro-Wilk W-Test for non- +% normality. Statistics and Computing, 2:117-119. +% 121–133. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_mvntest.m 6548 2015-09-11 12:39:47Z will $ + +alpha = 0.05; + +if nargin < 1, + error('Requires at least one input argument.'); + return; +end + +if nargin < 2 | isempty(df) + [n,p] = size(X); +else + n = df; + p = size(X,2); +end + +if (n <= 3), + error('n is too small.'); + return, +elseif (n >= 4) && (n <=11), + x = n; + g = -2.273 + 0.459*x; + m = 0.5440 - 0.39978*x + 0.025054*x^2 - 0.0006714*x^3; + s = exp(1.3822 - 0.77857*x + 0.062767*x^2 - 0.0020322*x^3); + for j = 1:p, + W(j) = ShaWilstat(X(:,j)); + Z(j) = (-log(g - (log(1 - W(j)))) - m)/s; + end +elseif (n >= 12) && (n <=2000), + x = log(n); + g = 0; + m = -1.5861 - 0.31082*x - 0.083751*x^2 + 0.0038915*x^3; + s = exp(-0.4803 -0.082676*x + 0.0030302*x^2); + for j = 1:p, + W(j) = ShaWilstat(X(:,j)); + Z(j) = ((log(1 - W(j))) + g - m)/s; + end +else + error('n is not in the proper size range.'); %error('n is too large.');return, + return, +end + +for j = 1:p, + R(j) = (norminv((normcdf( - Z(j)))/2))^2; +end + +u = 0.715; +v = 0.21364 + 0.015124*(log(n))^2 - 0.0018034*(log(n))^3; +l = 5; +C = corrcoef(X); %correlation matrix +NC = (C.^l).*(1 - (u*(1 - C).^u)/v); %transformed correlation matrix +T = sum(sum(NC)) - p; %total +mC = T/(p^2 - p); %average correlation +e = p/(1 + (p - 1)*mC); %equivalent degrees of freedom +H = (e*(sum(R)))/p; %Royston's statistic +P = 1 - chi2cdf(H,e); %P-value + +stats.p=P; +stats.W=W; +stats.Z=Z; +for j=1:p, + stats.k(j)=kurtosis(X(:,j)); + stats.s(j)=skewness(X(:,j)); + stats.pusw(j)=1-spm_Ncdf(stats.Z(j),0,1); + zj=(X(:,j)-mean(X(:,j)))/std(X(:,j)); + [h,pks]=kstest(zj); + stats.puks(j)=pks; +end + +%----------------------------------- +function [W] = ShaWilstat(x) +%SHAWILTEST Shapiro-Wilk' W statistic for assessing a sample normality. +% This m-file is generating from the Fortran Algorithm AS R94 (Royston, +% 1995) [URL address http://lib.stat.cmu.edu/apstat/181]. Here we take only +% the procedure to generate the Shapiro-Wilk's W statistic, needed to feed +% the Royston's test for multivariate normality. Here, we present both the +% options for the sample kurtosis type: 1) Shapiro-Francia for leptokurtic, +% and 2) Shapiro-Wilk for the platykurtic ones. Do not assume that the +% result of the Shapiro-Wilk test is clear evidence of normality or non- +% normality, it is just one piece of evidence that can be helpful. +% +% Input: +% x - data vector (3 < n < 5000) +% +% Output: +% W - Shapiro-Wilk's W statistic +% +% Reference: +% Scholz, F.W. and Stephens, M.A. (1987), K-Sample Anderson-Darling Tests. +% Journal of the American Statistical Association, 82:918-924. +% + +x = x(:); %put data in a column vector +n = length(x); %sample size + +if n < 3, +ois error('Sample vector must have at least 3 valid observations.'); +end + +if n > 5000, + warning('Shapiro-Wilk statistic might be inaccurate due to large sample size ( > 5000).'); +end + +x = sort(x); %sorting of data vector in ascending order +m = norminv(((1:n)' - 3/8) / (n + 0.25)); +w = zeros(n,1); %preallocating weights + +if kurtosis(x) > 3, %Shapiro-Francia test is better for leptokurtic samples + w = 1/sqrt(m'*m) * m; + W = (w' * x) ^2 / ((x - mean(x))' * (x - mean(x))); +else %Shapiro-Wilk test is better for platykurtic samples + c = 1/sqrt(m' * m) * m; + u = 1/sqrt(n); + p1 = [-2.706056,4.434685,-2.071190,-0.147981,0.221157,c(n)]; + p2 = [-3.582633,5.682633,-1.752461,-0.293762,0.042981,c(n-1)]; + + w(n) = polyval(p1,u); + w(1) = -w(n); + + if n == 3, + w(1) = 0.707106781; + w(n) = -w(1); + end + + if n >= 6, + w(n-1) = polyval(p2,u); + w(2) = -w(n-1); + + ct = 3; + phi = (m'*m - 2 * m(n)^2 - 2 * m(n-1)^2) / ... + (1 - 2 * w(n)^2 - 2 * w(n-1)^2); + else + ct = 2; + phi = (m'*m - 2 * m(n)^2) / (1 - 2 * w(n)^2); + end + + w(ct:n-ct+1) = m(ct:n-ct+1) / sqrt(phi); + + W = (w' * x) ^2 / ((x - mean(x))' * (x - mean(x))); +end + +return, \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_obsnoise.m b/toolbox/mci/inference/spm_mci_obsnoise.m new file mode 100644 index 00000000..bdf31c76 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_obsnoise.m @@ -0,0 +1,44 @@ +function [noise,M] = spm_mci_obsnoise (w,v,assign,noise,M,U,Y) +% Update observation noise +% FORMAT [noise,M] = spm_mci_obsnoise (w,v,assign,noise,M,U,Y) +% +% w random effects +% v fixed effects +% assign for dynamical models this structure specifies whether init +% states, flow and o/p params are random, fixed or known +% noise observation noise structure +% M model structures +% U input structures +% Y data structures +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_obsnoise.m 6548 2015-09-11 12:39:47Z will $ + +N=length(M); + +err=[]; +for n=1:N, + if ~isempty(w), wsub=w(:,n); else, wsub=[]; end + try ind=Y{n}.ind; catch ind=1:M{n}.N; end + if isfield(M{n},'IS') + % Other model type + yhat = feval(M{n}.IS,wsub,M{n},U{n}); + err=[err; Y{n}.y-yhat]; + else + % Differential equation model + [Pinit,Pflow] = spm_mci_init_flow (assign,wsub,v,M{n}); + M{n}.x0=Pinit; + yhat = spm_mci_fwd (Pflow,M{n},U{n}); + err=[err; Y{n}.y-yhat(ind,:)]; + end +end +NT=size(err,1); +noise.cN=noise.c0+0.5*NT; +noise.DN=noise.D0+0.5*NT*cov(err); +L=spm_wishrnd(noise.DN,noise.cN); +Ce=inv(L); +for n=1:N, + M{n}.Ce=Ce; +end \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_phm.m b/toolbox/mci/inference/spm_mci_phm.m new file mode 100644 index 00000000..3fae705f --- /dev/null +++ b/toolbox/mci/inference/spm_mci_phm.m @@ -0,0 +1,14 @@ +function [logev] = spm_mci_phm (L) +% Compute Log Evidence using Posterior Harmonic Mean (PHM) +% FORMAT [logev] = spm_mci_phm (L) +% +% L [S x 1] vector containing log-likelihood of samples +% logev log evidence from PHM +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_phm.m 6548 2015-09-11 12:39:47Z will $ + +Lbar=mean(L); +logev=-log(mean(exp(Lbar-L)))+Lbar; \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_pop.m b/toolbox/mci/inference/spm_mci_pop.m new file mode 100644 index 00000000..08698ce4 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_pop.m @@ -0,0 +1,479 @@ +function [P,logev,D,M] = spm_mci_pop (mcmc,M,U,Y) +% Population MCMC with Gaussian priors and proposals +% FORMAT [P,logev,D,M] = spm_mci_pop (mcmc,M,U,Y) +% +% mcmc Optimisation parameters eg. +% +% .J number of chains +% .gprob prob of global move +% .nscale number of scaling samples +% .ntune number of tuning samples +% .nsamp number samples (on avg) to return (per chain) +% .remove_burn_in Remove scale and tune samples. +% .init{j} [Np x 1] Initial parameter vector for jth chain [optional] +% +% M{i} Data structure for i=1st or i=2nd model. 1st model +% is the larger model. Specifying two models is only +% necessary if you wish to do model switch integration. +% Each M structure should contain +% +% .L Name of log-likelihood function eg. 'spm_dcm_like' +% must take arguments P,M,U,Y +% .pE Prior mean +% .pC Prior covariance +% .lambda1 Observation noise precision +% +% For example, if ith model does not have variable k +% one can set M{i}.pC(k,k)=1e-4; +% +% U{i} Input field for ith model (as standard) +% Y Data field +% +% For each chain the function implements the Adaptive Monte Carlo (AMC) +% algorithm which comprises three phases (i) scaling: proposal cov is +% optimally scaled prior (same scaling for all params), (ii) tuning: +% proposal cov is tuned using Robbins-Monro, (iii) sampling: the proposal +% is unchanged. At each stage proposals follow Metropolis-Hastings. +% +% The function returns +% +% P{j} Posterior samples from jth chain +% logev approximations to log evidence +% .pam Prior Arithmetic Mean +% .phm Posterior Harmonic Mean +% .ti Thermodynamic Integration +% +% For model switch integration logev.ti contains the log Bayes factor +% for model 2 versus model 1 +% +% D Diagnostics +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_pop.m 6548 2015-09-11 12:39:47Z will $ + +Nm=length(M); +if Nm>1 + model_switch=1; +else + model_switch=0; +end + +try + J=mcmc.J; +catch + J=64; +end + +try + gprob=mcmc.gprob; +catch + %gprob=0.25; + gprob=0.0; +end + +% Precompute some quantities for efficient computation of log prior +if model_switch + M = spm_mci_switch_prep (M); +else + M{1} = spm_mci_minit (M{1}); +end + +V = M{1}.V; +Np = size(V,1); +Nsub = size(V,2); + +if isstruct(M{1}.pC) + pC = full(diag(spm_vec(M{1}.pC))); +else + pC = M{1}.pC; +end + +try + anneal=mcmc.anneal; +catch + anneal='power'; +end + +% Set annealing schedule +switch anneal + case 'sigmoid', + % Good for model switching integration + x=linspace(-10,10,J); + beta=1./(1+exp(-x)); + case 'linear', + beta=linspace(0,1,J); + case 'log', + beta=logspace(log10(1/J),0,J-1); + beta=[0 beta]; + case 'nonlinear', + eta=0.2; + j=1:J; + beta=(eta*j/J)./(1-j/J+eta); + case 'power', + % From Calderhead & Girolami + beta=linspace(0,1,J); + beta=beta.^5; + case 'frozen', + beta=ones(1,J); +end + +for j=1:J, + P{j}.beta=beta(j); + P{j}.last_update=0; + P{j}.overall_accept=0; + P{j}.accept=0; + P{j}.reject=0; + P{j}.ar_traj=[]; +end + +% Initialise proposal covariance +try + for j=1:J, + P{j}.C=V'*mcmc.chain{j}.C*V; + end +catch + for j=1:J, + %P{j}.C=V'*M{1}.pC*V; + P{j}.C=V'*pC*V; + end +end + +% Initialise chains +for j=1:J, + if isfield(mcmc,'init') + % User specified + xinit=mcmc.init{j}; + else + % Drawn from prior + if model_switch & isfield(M{1},'Ep') + xinit=(1-beta(j))*M{1}.Ep+beta(j)*M{2}.Ep; + else + xinit=spm_normrnd(M{1}.vpE,M{1}.pC,1); + end + end + P{j}.theta = V'*(spm_vec(xinit)-M{1}.vpE); + Ep = P{j}.theta; + if model_switch + [q,q1,q2] = spm_mci_switch (Ep,M,U,Y,P{j}.beta); + P{j}.logq(1)=q; + P{j}.logq1(1)=q1; + P{j}.logq2(1)=q2; + else + [P{j}.logq(1),P{j}.loglike(1)] = spm_mci_joint (Ep,M{1},U{1},Y,P{j}.beta); + end +end + +try + nscale=mcmc.nscale; +catch + nscale=500; +end + +try + ntune=mcmc.ntune; +catch + ntune=500; +end + +try + nsamp=mcmc.nsamp; +catch + nsamp=1000; +end + +try + remove_burn_in=mcmc.remove_burn_in; +catch + remove_burn_in=1; +end + +% Reset counts for monitoring acceptance rates +for j=1:J, + P{j}.scale_accepted=0; + P{j}.scale_rejected=0; + P{j}.tune_accepted=0; + P{j}.tune_rejected=0; + P{j}.scaled=0; + P{j}.tuned=0; + P{j}.acc=1; +end + +try + verbose=mcmc.verbose; +catch + verbose=0; +end + +swaps=0; + +Ntot=(nscale+ntune+nsamp)*J; +if verbose + disp('Scaling ...'); +end +tic; +dL(1)=0; +for i=2:Ntot, + + % Local or Global move + m=rand(1); + if m < gprob + % Global Move + + % Now select a pair of chains with adjacent temperatures + jsel=floor(rand(1)*(J-1))+1; + + theta1=P{jsel}.theta(:,end); + theta2=P{jsel+1}.theta(:,end); + + like1=P{jsel}.logq(end); + like2=P{jsel+1}.logq(end); + + Ep1=theta1; + Ep2=theta2; + [A,Alike] = spm_mci_joint (Ep2,M,U,Y,P{jsel}.beta); + [B,Blike] = spm_mci_joint (Ep1,M,U,Y,P{jsel+1}.beta); + rg=exp(A+B-like1-like2); + + % Accept candidate ? + accept_prob=min(1,rg); + + if rand(1) < accept_prob + % Accept + P{jsel}.theta=[P{jsel}.theta theta2]; + P{jsel+1}.theta=[P{jsel+1}.theta theta1]; + P{jsel}.accept=P{jsel}.accept+1; + P{jsel+1}.accept=P{jsel+1}.accept+1; + + P{jsel}.logq(end+1)=A; + P{jsel+1}.logq(end+1)=B; + + P{j}.loglike(end+1)=Alike; + P{jsel+1}.loglike(end+1)=Blike; + swaps=swaps+1; + else + P{jsel}.theta=[P{jsel}.theta theta1]; + P{jsel+1}.theta=[P{jsel+1}.theta theta2]; + + P{jsel}.logq(end+1)=P{jsel}.logq(end); + P{jsel+1}.logq(end+1)=P{jsel+1}.logq(end); + + P{jsel}.loglike(end+1)=P{jsel}.loglike(end); + P{jsel+1}.loglike(end+1)=P{jsel+1}.loglike(end); + end + + + else + % Local Move + + % Now select a chain + jsel=floor(rand(1)*J)+1; + + if i update_sigma_steps + ar=sum(P{jsel}.acc(end-update_sigma_steps+1:end))/update_sigma_steps; + if ar < 0.2 + P{jsel}.C=0.5*P{jsel}.C; + if verbose + disp(sprintf('Acceptance Rate = %1.2f : Decreasing proposal width',ar)); + end + elseif ar > 0.4 + P{jsel}.C=2*P{jsel}.C; + if verbose + disp(sprintf('Acceptance Rate = %1.2f : Increasing proposal width',ar)); + end + else + if verbose + disp(sprintf('Acceptance Rate = %1.2f',ar)); + end + end + P{jsel}.last_update=0; + end + + elseif i>nscale*J & i < (ntune+nscale)*J + % Step 2 - tune + if P{jsel}.scaled==0 + if verbose + disp('Tuning ...'); + end + P{jsel}.scaled=1; + P{jsel}.adapt_its=1; + P{jsel}.Ct=P{jsel}.C; + P{jsel}.mu=mean(P{jsel}.theta,2); + + P{jsel}.scale_accepted=P{jsel}.accept; + P{jsel}.scale_rejected=P{jsel}.reject; + else + P{jsel}.adapt_its=P{jsel}.adapt_its+1; + end + P{jsel} = spm_mci_update_cov (P{jsel}); + + if mod(i-nscale,50)==0 + if verbose + disp(sprintf('Iteration %d out of %d',i-nscale,ntune)); + end + end + + elseif i>(ntune+nscale)*J + % Step 3 - sample + if P{jsel}.tuned==0 + if verbose + disp('Sampling ...'); + end + P{jsel}.tuned=1; + + % Eigendecomposition of P{jsel}.Ct + sc=2.4/sqrt(Nsub); + + if ntune > 0 + prop_cov=sc^2*P{jsel}.Ct; + else + prop_cov=sc^2*P{jsel}.C; + end + [vC,dC]=eig(prop_cov); + P{jsel}.Cs{1}=diag(dC); + P{jsel}.Cs{2}=vC; + + P{jsel}.tune_accepted=P{jsel}.accept-P{jsel}.scale_accepted; + P{jsel}.tune_rejected=P{jsel}.reject-P{jsel}.scale_rejected; + end + + if mod(i-nscale-ntune,50)==0 + if verbose + disp(sprintf('Iteration %d out of %d',i-nscale-ntune,nsamp)); + end + end + end + + % Generate candidate + if i<(nscale+ntune)*J+1 + % Scaling and tuning phases + sc=2.4/sqrt(Nsub); + prop_cov=sc^2*P{jsel}.C; + xcand=P{jsel}.theta(:,end)+sqrt(diag(prop_cov)).*randn(Nsub,1); + else + % Sampling phase + xcand=P{jsel}.theta(:,end)+spm_normrnd(zeros(Nsub,1),P{jsel}.Cs,1); + end + + % Candidates are already in reduced space + Epcand = xcand; + if model_switch + [cand_like,logq1,logq2] = spm_mci_switch (Epcand,M,U,Y,P{jsel}.beta); + else + [cand_like,loglike] = spm_mci_joint (Epcand,M{1},U{1},Y,P{jsel}.beta); + end + + % Accept candidate ? + dL(i)=cand_like-P{jsel}.logq(end); + accept_prob=min(1,exp(dL(i))); + + if rand(1) < accept_prob + % Accept + P{jsel}.theta=[P{jsel}.theta xcand]; + P{jsel}.logq(end+1)=cand_like; + P{jsel}.accept=P{jsel}.accept+1; + P{jsel}.overall_accept=P{jsel}.overall_accept+1; + + if model_switch + P{jsel}.logq1(end+1)=logq1; + P{jsel}.logq2(end+1)=logq2; + else + P{jsel}.loglike(end+1)=loglike; + end + + P{jsel}.acc=[P{jsel}.acc;1]; + + else + % Reject + P{jsel}.theta(:,end+1)=P{jsel}.theta(:,end); + P{jsel}.logq(end+1)=P{jsel}.logq(end); + P{jsel}.reject=P{jsel}.reject+1; + + if model_switch + P{jsel}.logq1(end+1)=P{jsel}.logq1(end); + P{jsel}.logq2(end+1)=P{jsel}.logq2(end); + else + P{jsel}.loglike(end+1)=P{jsel}.loglike(end); + end + + P{jsel}.acc=[P{jsel}.acc;0]; + end + + end +end + + +burn_in=nscale+ntune; +if remove_burn_in + % Remove burn-in samples from each chain + for j=1:J, + P{j}.theta=P{j}.theta(:,burn_in+1:end); + P{j}.logq=P{j}.logq(burn_in+1:end); + if model_switch + P{j}.logq1=P{j}.logq1(burn_in+1:end); + P{j}.logq2=P{j}.logq2(burn_in+1:end); + else + P{j}.loglike=P{j}.loglike(burn_in+1:end); + end + end +end + +% Project parameters from eigenspace of prior back into original space +for j=1:J, + nj=size(P{j}.theta,2); + P{j}.theta=spm_vec(M{1}.pE)*ones(1,nj)+V*P{j}.theta; +end + +for j=1:J, + P{j}.dL=dL; + P{j}.ar=P{j}.overall_accept/(burn_in+nsamp); + D.ar(j)=P{j}.ar; + + P{j}.ar_scale=P{j}.scale_accepted/(P{j}.scale_accepted+P{j}.scale_rejected); + P{j}.ar_tune=P{j}.tune_accepted/(P{j}.tune_accepted+P{j}.tune_rejected); + + sample_accepted=P{j}.overall_accept-P{j}.scale_accepted-P{j}.tune_accepted; + sample_rejected=P{j}.reject-P{j}.scale_rejected-P{j}.tune_rejected; + P{j}.ar_sample=sample_accepted/(sample_accepted+sample_rejected); + beta(j)=P{j}.beta; + + if model_switch + Uhat(j)=mean(P{j}.logq2-P{j}.logq1); + else + Uhat(j)=mean(P{j}.loglike); + end +end + +% Compute evidence using trapezoidal rule: +% Lartillot and Phillipe (2006) Computing Bayes Factors Using Thermodynamic +% Integration, Systematic Biology, 2006, 55(2):195-207. +if J>1 + logev.ti=trapz(beta,Uhat); +else + logev.ti=Uhat; +end + +D.swaps=swaps; +D.Uhat=Uhat; +D.beta=beta; + +% For frozen annealing (ie plain MH) compute overall PHM +if strcmp(anneal,'frozen') + L=[];Lq=[]; + for j=1:J, + L=[L, P{j}.loglike]; + Lq=[Lq, P{j}.logq]; + end + logev.phm = spm_mci_phm (L); +else + logev.phm = []; +end + +D.els=toc; + + diff --git a/toolbox/mci/inference/spm_mci_popdef.m b/toolbox/mci/inference/spm_mci_popdef.m new file mode 100644 index 00000000..2d3ae4e8 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_popdef.m @@ -0,0 +1,17 @@ +function [mh] = spm_mci_popdef (scale,tune,samp) +% Set default parameters for population MCMC +% FORMAT [mh] = spm_mci_popdef (scale,tune,samp) +%__________________________________________________________________________ +% Copyright (C) 2014 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_popdef.m 6548 2015-09-11 12:39:47Z will $ + +mh.nscale=scale; +mh.ntune=tune; +mh.nsamp=samp; +mh.ind_samp=[scale+tune+1:scale+tune+samp]; +mh.J=1; % Number of temperatures +mh.gprob=0; +mh.remove_burn_in=0; +mh.verbose=0; diff --git a/toolbox/mci/inference/spm_mci_post.m b/toolbox/mci/inference/spm_mci_post.m new file mode 100644 index 00000000..009fb654 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_post.m @@ -0,0 +1,274 @@ +function [post] = spm_mci_post (mcmc,M,U,Y,true_P) +% Estimate posterior density +% FORMAT [post] = spm_mci_post (mcmc,M,U,Y,true_P) +% +% mcmc .inference = 'amc','ais','vl' or 'langevin' +% .verbose = 0 or 1 to plot progress (default 0) +% .maxits = max number of iterations for sampling +% .init = init parameter values (default is prior mean) +% M model structure +% U inputs (shouldn't be empty) +% Y data +% true_P true parameters (if known) +% +% post structure containing posterior (mean, samples etc) +%__________________________________________________________________________ +% Copyright (C) 2014 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_post.m 6548 2015-09-11 12:39:47Z will $ + +try verbose=mcmc.verbose; catch verbose=0; end +if nargin < 5 | isempty(true_P) + tp=0; +else + tp=1; +end + +tic; +switch mcmc.inference, + + case 'ais', + disp('Annealed Importance Sampling'); + disp(' '); + + mcmc.nsamp=mcmc.maxits; + + tic; + post = spm_mci_ais (mcmc,M,U,Y); + toc + + Nsamp=size(post.P,2); + post.ind=[1:Nsamp]; + post.Ep=mean(post.P(:,post.ind)')'; + post.mcmc=mcmc; + + % Eigenrep needed for spm_mci_joint later + M = spm_mci_minit (M); + + case 'multi-amc', + disp('Multiple chains of Adaptive Monte Carlo'); + disp(' '); + Nsamp=ceil(0.5*mcmc.J); + Nscale=0; + Ntune=Nsamp; + mc = spm_mci_popdef (Nscale,Ntune,Nsamp); + mc.verbose=verbose; + + MM{1}=M; + UU{1}=U; + + for it=1:mcmc.maxits, + % Loop over chains + mcmc.init{1}=spm_normrnd(M.pE,M.pC,1); + Psamp = spm_mci_pop (mc,MM,UU,Y); + if it ==1 + P=Psamp{1}.theta; + else + P=[P,Psamp{1}.theta]; + end + end + post.ind=[1:size(P,2)]; + post.Ep=mean(P(:,post.ind)')'; + post.P=P; + post.mcmc=mcmc; + + % Eigenrep needed for spm_mci_joint later + M = spm_mci_minit (M); + + case 'amc', + disp('Adaptive Monte Carlo'); + disp(' '); + + % MH defaults + tune=1; + if tune + Nscale=ceil(0.25*mcmc.maxits); + Ntune=Nscale; + Nsamp=ceil(0.5*mcmc.maxits); + else + Nscale=ceil(0.5*mcmc.maxits); + Ntune=0; + Nsamp=Nscale; + end + mc = spm_mci_popdef (Nscale,Ntune,Nsamp); + mc.verbose=verbose; + + % Draw initialisation point from prior ? + %mcmc.init{1}=spm_normrnd(M.pE,M.pC,1); + try mc.init{1}=mcmc.init; catch mc.init{1}=spm_vec(M.pE); end + + MM{1}=M; + UU{1}=U; + tic; + [Psamp,logev,D,MM] = spm_mci_pop (mc,MM,UU,Y); + toc + M=MM{1}; + + if tp + [post.Ep,post.SDp]=spm_mci_report (Psamp,mc,true_P); + else + disp('Initial params:'); + disp(mc.init{1}); + [post.Ep,post.SDp]=spm_mci_report (Psamp,mc); + end + + post.Ep=post.Ep'; + post.P=Psamp{1}.theta; + post.E=-Psamp{1}.logq; + post.dL=Psamp{1}.dL; + post.bayes_fb=zeros(1,length(post.E)); + post.acc=Psamp{1}.acc; + post.logev=logev; + post.D=D; + post.mcmc=mcmc; + post.ind=mc.ind_samp; + + case 'vl', + disp('Variational Laplace'); + disp(' '); + + D.y=Y; + if ~verbose + M.nograph=1; + end + + if isstruct(U) + UI=U; + else + UI.u=U'; + UI.dt=M.T/M.N; + end + + % Change VL defaults + if isfield(mcmc,'maxits'), M.Nmax=mcmc.maxits; end + if isfield(mcmc,'init'), M.P=mcmc.init; end + if isfield(mcmc,'hE'), M.hE=mcmc.hE; end + if isfield(mcmc,'hC'), M.hC=mcmc.hC; end + + + [Ep,Cp,Eh,F,tmp1,tmp2,tmp3,k] = spm_nlsi_GN (M,UI,D); + + post.Ep=spm_vec(Ep); + post.Cp=Cp; + post.Eh=Eh; + post.Ce=diag(1./exp(Eh)); + post.logev=F; + post.its=k; + if isfield(M,'P') + post.init=M.P; + end + + % Eigenrep needed for spm_mci_joint later + M = spm_mci_minit (M); + + case 'langevin', + disp('Langevin Monte Carlo'); + disp(' '); + + [M,stats]=spm_mci_lgv(mcmc,M,U,Y); + Psamp=stats.P'; + Nsamp=size(Psamp,1); + burn_in=round(0.3*size(Psamp,1)); + post=stats; + post.ind=[burn_in+1:Nsamp]; + post.targ=Psamp(post.ind,:); + post.Ep=mean(post.targ)'; + + % 2.5%, 50% and 97.5% quantiles + q = [.025 .5 .975]; + for j=1:M.Np, + sx = post.P(j,post.ind); + post.quantiles(j,:) = quantile(sx,q); + end + + otherwise + disp('Unknown inference method'); +end + +if strcmp(mcmc.inference,'VL') + Up=UI; +else + Up=U; +end + +% Generate data fit from posterior mean +if isfield(M,'IS') + if strcmp(M.IS,'spm_gen_erp') + Ps=spm_unvec(post.Ep,M.pE); + post.Yhat=feval('spm_gen_erp',Ps,M,Up); + else + post.Yhat = feval(M.IS,post.Ep,M,Up); + end +else + post.Yhat = spm_mci_fwd (post.Ep,M,Up); +end + +post.els=toc; +disp(sprintf('Optimisation time = %1.2f seconds',post.els)); + +lw=2; +if ~isfield(M,'IS') + % Plot time series + figure + rm=ceil(sqrt(M.l)); + for i=1:M.l, + if M.l>3 + subplot(rm,rm,i); + else + subplot(M.l,1,i); + end + plot(M.t,Y(:,i),'LineWidth',lw); + hold on + plot(M.t,post.Yhat(:,i),'r','LineWidth',lw); + grid on + set(gca,'FontSize',16); + legend('Data','Fit'); + xlabel('Time'); + ylabel(sprintf('y(%d)',i)); + end +end + +% get parameters in reduced space +Pr=M.V'*(post.Ep-M.vpE); +post.L_est = spm_mci_joint (Pr,M,U,Y); +disp(sprintf('Estimated Log Joint=%1.2f',post.L_est)); + +if tp + % get parameters in reduced space + Pr=M.V'*(spm_vec(true_P)-M.vpE); + post.L_true = spm_mci_joint (Pr,M,U,Y); + disp(sprintf('True Log Joint=%1.2f',post.L_true)); + disp(' '); +end + +pt=4; +if M.Np > pt + hp=figure; + set(hp,'Name','Parameters'); + plot(post.Ep,'r','LineWidth',lw); + xlabel('Parameter'); + set(gca,'FontSize',16); + grid on +else + disp('Estimated (latent) params:'); + disp(post.Ep); +end + +if tp + if M.Np > pt + hold on + plot(spm_vec(true_P),'LineWidth',lw); + legend('Estimated','True'); + else + disp('True (latent) params:'); + disp(spm_vec(true_P)); + end +end + +switch mcmc.inference, + case {'amc','langevin'}, + post.type='sample'; + otherwise + post.type='gaussian'; +end \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_postslices.m b/toolbox/mci/inference/spm_mci_postslices.m new file mode 100644 index 00000000..9c35092e --- /dev/null +++ b/toolbox/mci/inference/spm_mci_postslices.m @@ -0,0 +1,61 @@ +function [x,pnum,pgauss] = spm_mci_postslices (post,M,U,Y,Nbins) +% Univariate slices through posterior density +% FORMAT [x,pnum,pgauss] = spm_mci_postslices (post,M,U,Y,Nbins) +% +% post posterior data structure +% M,U,Y as usual +% Nbins Number of bins per dimension +% +% x [Np x Nbins] matrix where x(p,:) is domain for pth variable +% pnum [Np x Nbins] where pnum(p,j) = p(x(p)=xj|x(\p),Y) ie. the posterior +% density of variable p conditioned on the posterior mean of the other +% variables. This is estimated numerically from evaluation of log joint +% pgauss As pnum but under assumption that posterior is multivariate Gaussian +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_postslices.m 6548 2015-09-11 12:39:47Z will $ + +try Nbins=Nbins; catch Nbins=50; end + +k=5; % Defines width of domain in number of SDs + +% For computing log prior term +M = spm_mci_priors (M); +M = spm_mci_minit (M); + +% Need high tolerances to avoid drop-outs in log-joint plots +M.reltol=1e-6; +M.abstol=1e-6; + +Np=length(post.Ep); +Ep=post.Ep; +s=sqrt(diag(post.Cp)); +pE=spm_vec(M.pE); +for p=1:Np, + xmin=Ep(p)-k*s(p); + xmax=Ep(p)+k*s(p); + x(p,:)=linspace(xmin,xmax,Nbins); + P = Ep; + for j=1:Nbins, + % Get parameters in eigenspace of prior + P(p) = x(p,j); + par = M.V'*(P-pE); + eq(j) = spm_mci_joint (par,M,U,Y); + pg(j) = spm_mvNpdf(P,Ep,post.Cp); + end + eq=exp(eq); + pnum(p,:)=eq/sum(eq); + pgauss(p,:)=pg/sum(pg); +end + +figure +for i=1:Np, + subplot(Np,1,i); + plot(x(i,:),pgauss(i,:),'r'); + hold on + plot(x(i,:),pnum(i,:),'k'); + legend('Gaussian','Numeric'); + xlabel(sprintf('P(%d)',i)); +end \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_priors.m b/toolbox/mci/inference/spm_mci_priors.m new file mode 100644 index 00000000..455aad62 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_priors.m @@ -0,0 +1,29 @@ +function [M] = spm_mci_priors (M) +% Quantities for computing log prior in subspace +% FORMAT [M] = spm_mci_priors (M) +% +% M.V projection matrix +% M.ipC Inverse prior cov in reduced space +% M.log_prior_t2 second term of log prior +% M.Np dimension of reduced space +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_priors.m 6548 2015-09-11 12:39:47Z will $ + +if isstruct(M.pC) + pC=full(diag(spm_vec(M.pC))); +else + pC = M.pC; +end +V = spm_svd(pC,exp(-32)); +Np = size(V,2); +pC = V'*pC*V; +ipC = inv(pC); +log_prior_t2 = spm_logdet(ipC)/2-0.5*Np*log(2*pi); + +M.ipC=ipC; +M.V=V; +M.log_prior_t2=log_prior_t2; +M.Np=Np; diff --git a/toolbox/mci/inference/spm_mci_progress.m b/toolbox/mci/inference/spm_mci_progress.m new file mode 100644 index 00000000..0b15646a --- /dev/null +++ b/toolbox/mci/inference/spm_mci_progress.m @@ -0,0 +1,27 @@ +function [] = spm_mci_progress (x,E,i) +% Plot trajectories of parameters and neg log joint +% FORMAT [] = spm_mci_progress (x,E,i) +% +% x parameters +% E Energy = Neg Log Joint +% i iteration number +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_progress.m 6548 2015-09-11 12:39:47Z will $ + +sample_win=64; +min_it=max(i-sample_win+1,1); +its=[min_it:i-1]; +display(['It: ', num2str(i)]); +subplot(1,2,1); plot(its,x(its,:)); +xlabel('Sample','FontName','Arial','FontSize',16); +ylabel('Params','FontName','Arial','FontSize',16);box off; +set(gca,'FontSize',16,'FontName','Arial');axis tight; + +subplot(1,2,2); plot(its,E(its)); +xlabel('Sample','FontName','Arial','FontSize',16); +ylabel('E','FontName','Arial','FontSize',16);box off; +set(gca,'FontSize',16,'FontName','Arial');axis tight; +drawnow; diff --git a/toolbox/mci/inference/spm_mci_quantiles.m b/toolbox/mci/inference/spm_mci_quantiles.m new file mode 100644 index 00000000..cb30297e --- /dev/null +++ b/toolbox/mci/inference/spm_mci_quantiles.m @@ -0,0 +1,48 @@ +function [y] = spm_mci_quantiles (post,j,q3,expP) +% Plot histogram and quantiles of posterior density +% FORMAT [y] = spm_mci_quantiles (post,j,q3,expP) +% +% post posterior data structure +% j jth variate +% q3 plot quantiles on histogram +% expP exponentiate parameters before plotting ? +% +% y 2.5%, 50%, 97.5% quantiles +% +% Solid lines show quantiles from posterior samples +% Dotted lines under Gaussian assumptions +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_quantiles.m 6548 2015-09-11 12:39:47Z will $ + +try plotq=q3; catch plotq=1; end +try expP=expP; catch expP=0; end + +q = [.025 .5 .975]; +N = length(q); +x = post.P(j,post.ind); +if expP, x=exp(x); end +y = quantile(x,q); +%figure +hist(x); +hold on +yl=get(gca,'YLim'); +ym=yl(2); + +h = findobj(gca,'Type','patch'); +set(h,'FaceColor', [.5 .5 .5]); + +m=mean(x); +s=std(x); +yg=spm_invNcdf(q,m,s^2); + +%cols={'k','b','r','b','k'}; +if plotq + lw=2; + for i = 1:N, + plot([y(i) y(i)],[0 ym],'k','LineWidth',lw); + plot([yg(i) yg(i)],[0 ym],'k:','LineWidth',lw); + end +end diff --git a/toolbox/mci/inference/spm_mci_random.m b/toolbox/mci/inference/spm_mci_random.m new file mode 100644 index 00000000..2475d7a7 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_random.m @@ -0,0 +1,135 @@ +function [S] = spm_mci_random (mcmc,R,v,M,U,Y) +% Random effects estimation +% FORMAT [S] = spm_mci_random (mcmc,R,v,M,U,Y) +% +% mcmc Sampling parameters +% R Priors on random effects (R.pE, R.pC) +% v Fixed effects +% M Model Structure (single subject) +% U Inputs (single subject) +% Y Data (single subject) +% +% S Samples, [maxits x M.n] +% +% Uses Langevin Monte Carlo +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_random.m 6548 2015-09-11 12:39:47Z will $ + +% Data precision + +try verbose=mcmc.verbose; catch verbose=0; end +try maxits=mcmc.maxits; catch maxits=64; end +try plot_int=mcmc.plot_int; catch plot_int=1; end +try h=mcmc.h; catch h=0.5; end + +% Assign init/flow/out params as fixed/random effects +try assign=mcmc.assign; catch assign=[]; end + +% Compute eigen-parameterisation +M = spm_mci_minit (M); + +try init=mcmc.init; catch init=R.pE; end +% Initial param in eigenspace +xinit = init; + +% Read data points and time indices +try ind=Y.ind; catch ind=1:M.N; end +Nt=length(ind); +y=Y.y; + +% Sample matrix +Nx=size(R.pE,1); +x = zeros(maxits,Nx); +x(1,:) = xinit'; + +ipC=pinv(R.pC); +logdet_RCp=spm_logdet(R.pC); + +if verbose figure; end + +% Tune h by monitoring acceptance rate +tune_h=1; +acc_block=64; +acc_low=0.3; +acc_high=0.7; +total_acc_target=64; % Number of accepted samples to get +acc=zeros(maxits,1); + +i=1; +while (i <= maxits), + + if verbose + if mod(i,plot_int) == 0 & i > 2 + spm_mci_progress (x,E,i); + end + end + + if mod(i,acc_block)==0 & tune_h + % Change step size h ? + Nacc=sum(acc(i-acc_block+1:i-1)); + prop_acc=Nacc/acc_block; + if prop_acc < acc_low + if verbose, disp('Decreasing step size ...'); end + h=h/2; + elseif prop_acc > acc_high + if verbose, disp('Increasing step size ...'); end + h=h*2; + end + end + + % Proposal (first proposal always accepted) + if i==1 + pos=x(1,:)'; + curr=[]; + else + pos=spm_normrnd(curr.mu,curr.Cp,1); + end + + % Quantities re proposal + prop.pos=pos; + if isfield(M,'IS') + % Other model types + [j,iCpY,st,L] = spm_mci_joint_grad (pos,M,U,Y); + else + % Differential equation models + [dLdp,iCpY,st] = spm_mci_grad_curve (assign,pos,v,M,U,Y,'random'); + % Gradient of log prior + ep = pos-R.pE; + dlogprior=-ep'*ipC; + j=dLdp+dlogprior; + + [p_init,p_flow] = spm_mci_init_flow (assign,pos,v,M); + log_like = spm_mci_like_ind (p_flow,p_init,M,U,Y); + log_prior = -0.5*ep'*ipC*ep + logdet_RCp-0.5*M.Np*log(2*pi); + L=log_like+log_prior; + end + + if st==-1 + disp('Integration problem in spm_mci_random.m'); + keyboard + end + prop.L = L; + + % Posterior covariance under local linear approximation + prop.Cp = h*inv(iCpY+ipC); + prop.mu = pos+0.5*h*prop.Cp*j(:); + + prop.iCp = inv(prop.Cp); + prop.logdetCp = spm_logdet(prop.Cp); + + % Accept proposal ? + [curr,accepted,bayes_fb(i),dL(i)] = spm_mci_mh_update(curr,prop,verbose); + acc(i)=accepted; + E(i) = -curr.L; + if i > 1 + dEdit(i-1)=100*(E(i)-E(i-1))/E(i-1); + end + x(i,:)=curr.pos; + + i=i+1; +end + +S=x(1:i-1,:)'; diff --git a/toolbox/mci/inference/spm_mci_report.m b/toolbox/mci/inference/spm_mci_report.m new file mode 100644 index 00000000..2c7adad6 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_report.m @@ -0,0 +1,84 @@ +function [Ep,SDp] = spm_mci_report (P,mcmc,true_P) +% Report on posterior density from MCI +% FUNCTION [Ep,SDp] = spm_mci_report (P,mcmc,true_P) +% +% P Samples +% mcmc Sampling options +% +% Ep Posterior mean +% SDp Posterior SD +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_report.m 6548 2015-09-11 12:39:47Z will $ + +if nargin < 3 | isempty(true_P) + tp=0; +else + tp=1; +end + +Np=size(P{1}.theta,1); + +epoch={'Scale','Tune','Sample'}; +index(1).ind=[1:mcmc.nscale]; +index(2).ind=[mcmc.nscale+1:mcmc.nscale+mcmc.ntune]; +index(3).ind=[mcmc.nscale+mcmc.ntune+1:mcmc.nscale+mcmc.ntune+mcmc.nsamp]; + +if Np==2 + figure + for i=1:3, + subplot(2,2,i); + + theta1=P{1}.theta(1,index(i).ind); + theta2=P{1}.theta(2,index(i).ind); + + plot(theta1,theta2,'k.'); + grid on + title(sprintf('%s',epoch{i})); + xlabel('P_1'); + ylabel('P_2'); + + hold on + if isfield(mcmc,'init') + Pi=mcmc.init{1}; + if i==1 + plot(Pi(1),Pi(2),'bo'); + end + end + if tp + plot(true_P(1),true_P(2),'ro'); + end + end +end + +disp(sprintf('Scaling acceptance rate = %1.2f',P{1}.ar_scale)); +disp(sprintf('Tuning acceptance rate = %1.2f',P{1}.ar_tune)); +disp(sprintf('Sampling acceptance rate = %1.2f',P{1}.ar_sample)); + +% disp('Scaled covariance - used in tuning stage'); +% P{1}.C +% +% if isfield(P{1},'Ct') +% disp('Tuned covariance - used in sampling stage'); +% P{1}.Ct +% end + +% figure +% for i=1:Np, +% subplot(Np,1,i); +% plot(P{1}.theta(i,:)); +% grid on +% ylabel(sprintf('P(%d)',i)); +% end +% xlabel('Samples'); + +x=P{1}.theta(:,index(3).ind); +Ep=mean(x'); +SDp=std(x'); + +%disp('Posterior mean:'); +%Ep +%disp('Posterior SD:'); +%SDp diff --git a/toolbox/mci/inference/spm_mci_stat.m b/toolbox/mci/inference/spm_mci_stat.m new file mode 100644 index 00000000..55c93b9e --- /dev/null +++ b/toolbox/mci/inference/spm_mci_stat.m @@ -0,0 +1,118 @@ +function [pstat,mu,nse,batch] = spm_mci_stat (post,nbatch,method) +% Test MCMC samples for stationarity +% FORMAT [pstat,mu,nse,batch] = spm_mci_stat (post,nbatch,method) +% +% post posterior distribution +% nbatch number of batches (last batch contains last half of samples) +% method 'geweke', 'ar1' (default) or 'geyer' +% +% pstat p-value for batch mean being different to final batch mean +% mu batch mean (of energy) +% nse batch numeric standard error (of energy) +% batch (n).ind, (n).N indices and number of samples in nth batch +% +% This routine is based on Geweke 1992. But we also allow estimates +% of the Numeric Standard Error (NSE) to be estimated using an AR1 model +% or Geyer's method +% +% J. Geweke (1992) Evaluating the accuracy of sampling-base approaches to +% the calculation of posterior moments. Bayesian Statistics 4, OUP. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_stat.m 6548 2015-09-11 12:39:47Z will $ + +try meth=method; catch meth='ar1'; end +try Nb=nbatch; catch Nb=6; end +Ns=size(post.P,2); + +% Define batches +bl=floor(0.5*Ns/(Nb-1)); +boffset=0; +for b=1:Nb-1, + batch(b).ind=[1:bl]+boffset; + boffset=boffset+bl; +end +batch(Nb).ind=boffset+1:Ns; + +% Get Means and NSE for each batch +for b=1:Nb, + ind=batch(b).ind; + batch(b).N=length(ind); + E=post.E(ind); + mu(b)=mean(E); + N(b)=length(E); + switch meth + + case 'geweke', + [spec,f] = spectrum(E-mean(E)); + s0 = spec(1); + nse(b)=sqrt(s0); + + case 'geyer', + % Geyer's method + ess=spm_mci_ess(E); + nse(b)=std(E)*sqrt(N(b))/sqrt(ess); + + case 'ar1', + % AR(1) method + ar=spm_ar(E,1); + nse(b)=sqrt(1/(ar.mean_beta)); + end +end + +% Are first Nb-1 batch means different to final batch mean ? +for b=1:Nb-1, + % Geweke's test statistic is a z-score + d = mu(b)-mu(Nb); + s2 = nse(b)^2+nse(Nb)^2; + pbigger = 1-spm_Ncdf(abs(d),0,s2); + psmaller = spm_Ncdf(-abs(d),0,s2); + pstat(b)=pbigger+psmaller; +end + +end + + +% ------------------------------------------------------------ + +function [y,f]=spectrum(x,nfft,nw) +%SPECTRUM Power spectral density using Hanning window +% [y,f]=spectrum(x,nfft,nw) + +if nargin < 2 | isempty(nfft) + nfft = min(length(x),256); +end +if nargin < 3 | isempty(nw) + nw = fix(nfft/4); +end +noverlap = fix(nw/2); + +% Hanning window +w = .5*(1 - cos(2*pi*(1:nw)'/(nw+1))); + +n = length(x); +if n < nw + x(nw)=0; n=nw; +end +x = x(:); + +k = fix((n-noverlap)/(nw-noverlap)); % no of windows +index = 1:nw; +kmu = k*norm(w)^2; % Normalizing scale factor +y = zeros(nfft,1); +for i=1:k + xw = w.*x(index); + index = index + (nw - noverlap); + Xx = abs(fft(xw,nfft)).^2; + y = y + Xx; +end + +y = y*(1/kmu); % normalize + +n2 = floor(nfft/2); +y = y(1:n2); +f = 1./n*(0:(n2-1)); + +end diff --git a/toolbox/mci/inference/spm_mci_switch.m b/toolbox/mci/inference/spm_mci_switch.m new file mode 100644 index 00000000..05cf221a --- /dev/null +++ b/toolbox/mci/inference/spm_mci_switch.m @@ -0,0 +1,27 @@ +function [logp,logq1,logq2] = spm_mci_switch (Pr,M,U,Y,beta) +% Return log probability of tempered model switch +% FORMAT [logp,logq1,logq2] = spm_mci_switch (Pr,M,U,Y,beta) +% +% Pr parameters (vectorised and in M.V subspace) +% M,U,Y as usual +% beta inverse temperature (set to 1 to get usual posterior) +% +% logp log prob of model switch +% logq1 log joint of model 1 +% logq2 log joint of model 2 +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_switch.m 6548 2015-09-11 12:39:47Z will $ + +logq1 = spm_mci_joint(Pr,M{1},U{1},Y); + +% Parameters in original space +P = M{1}.V*Pr+M{1}.vpE; +[L2,tmp,st] = feval(M{2}.L,P,M{2},U{2},Y); +e = P-M{2}.vpE; +L1 = - e'*M{2}.ipC*e/2 + M{2}.log_prior_t2; +logq2 = L1+L2; + +logp=(1-beta)*logq1+beta*logq2; diff --git a/toolbox/mci/inference/spm_mci_switch_prep.m b/toolbox/mci/inference/spm_mci_switch_prep.m new file mode 100644 index 00000000..67eca7b6 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_switch_prep.m @@ -0,0 +1,45 @@ +function [M] = spm_mci_switch_prep (M) +% Prepare quantities for computing log prior in SVD-reduced space +% FORMAT [M] = spm_mci_switch_prep (M) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_switch_prep.m 6548 2015-09-11 12:39:47Z will $ + +for i=1:2, + if isstruct(M{i}.pE) + M{i}.vpE=spm_vec(M{i}.pE); + else + M{i}.vpE=M{i}.pE; + end + if isfield(M{i},'Ce') + M{i}.logdet_Ce=spm_logdet(M{i}.Ce); + try + M{i}.iCe=M{i}.iCe; + catch + M{i}.iCe = inv(M{i}.Ce); + end + end +end + + +% Big model + +pC = M{1}.pC; +Np = size(pC,1); +V = spm_svd(pC,exp(-32)); +pC = V'*pC*V; +ipC = inv(pC); +log_prior_t2 = spm_logdet(ipC)/2-0.5*Np*log(2*pi); + +M{1}.ipC=ipC; +M{1}.V=V; +M{1}.log_prior_t2=log_prior_t2; + +% Small model +ipC = inv(M{2}.pC); +log_prior_t2 = spm_logdet(ipC)/2-0.5*Np*log(2*pi); + +M{2}.ipC=ipC; +M{2}.log_prior_t2=log_prior_t2; \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mci_update_cov.m b/toolbox/mci/inference/spm_mci_update_cov.m new file mode 100644 index 00000000..25e65ec2 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_update_cov.m @@ -0,0 +1,22 @@ +function [P] = spm_mci_update_cov (P) +% Update covariance matrix of proposal density using Robbins-Monro +% FORMAT [P] = spm_mci_update_cov (P) +% +% See e.g. +% H. Haario, E. Saksman, and J. Tamminen. An adaptive Metropolis algorithm. +% Bernoulli, 7(2):223–242, 2001. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_update_cov.m 6548 2015-09-11 12:39:47Z will $ + +Np=size(P.theta,1); +gamma=1/P.adapt_its; + +x=P.theta(:,end); +dx=x-P.mu; + +P.mu=P.mu+gamma*dx; +P.Ct=P.Ct+gamma*(dx*dx'-P.Ct); + diff --git a/toolbox/mci/inference/spm_mci_vw_init.m b/toolbox/mci/inference/spm_mci_vw_init.m new file mode 100644 index 00000000..38a56652 --- /dev/null +++ b/toolbox/mci/inference/spm_mci_vw_init.m @@ -0,0 +1,128 @@ +function [w_init,v_init,assign,update_ffx,update_rfx] = spm_mci_vw_init (MCI) +% Initialise fixed and random effects +% FORMAT [w_init,v_init,assign,update_ffx,update_rfx] = spm_mci_vw_init (MCI) +% +% MCI MCI data structure +% +% w_init initial rfx values +% v_init initial ffx values +% assign data structure describing how rfx/ffx are assigned +% to initial conditions, flow and output params +% update_ffx (1/0) +% update_rfx (1/0) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mci_vw_init.m 6548 2015-09-11 12:39:47Z will $ + +assign=MCI.assign; +try fixed=MCI.fixed; catch fixed=[]; end +S=MCI.S; + +Np=length(spm_vec(MCI.M{1}.pE)); +Nflow=MCI.M{1}.Npflow; +Nout=MCI.M{1}.Npout; + +if isfield(MCI.M{1},'x0') + % For dynamical systems + d=size(MCI.M{1}.x0,1); +end + +% First part of w/v vector is for initial condition parameters +% Second part for flow parameters +% Third part for output parameters +w_init=[];v_init=[]; +switch assign.init_par, + case 'random', + assign.w_init=[1:d]; + assign.v_init=[]; + if isfield(MCI,'pinit0'); + w_init=MCI.pinit0; + end + case 'fixed', + assign.v_init=[1:d]; + assign.w_init=[]; + if isfield(MCI,'pinit0'); + v_init=MCI.pinit0; + end + otherwise + % Assume 'known' + assign.v_init=[]; + assign.w_init=[]; +end + +switch assign.flow_par, + case 'random', + assign.w_flow=length(assign.w_init)+[1:Nflow]; + assign.v_flow=[]; + if isfield(MCI,'pflow0'); + w_init=[w_init;MCI.pflow0]; + end + case 'fixed', + assign.v_flow=length(assign.v_init)+[1:Nflow]; + if isfield(MCI,'pflow0'); + v_init=[v_init;MCI.pflow0]; + end + assign.w_flow=[]; + otherwise + % Assume 'known' + assign.v_flow=[]; + assign.w_flow=[]; +end + +switch assign.out_par, + case 'random', + assign.w_out=length(assign.w_init)+length(assign.w_flow)+[1:Nout]; + assign.v_out=[]; + if isfield(MCI,'pout0'); + w_init=[w_init;MCI.pout0]; + end + case 'fixed', + assign.v_out=length(assign.v_init)+length(assign.v_flow)+[1:Nout]; + if isfield(MCI,'pout0'); + v_init=[v_init;MCI.pout0]; + end + assign.w_out=[]; + otherwise, + % Assume 'known' + assign.v_out=[]; + assign.w_out=[]; +end + +if ~isempty(fixed) + % Initialise fixed effects + if isempty(v_init) + v_init=spm_normrnd(fixed.vpE,fixed.pC,1); + end +end + +% Initialise random effects +if isempty(w_init) + m=S.prior.m; + C=S.prior.B/S.prior.a; + w_init=spm_normrnd(m,C,S.N); +end + + +% Update RFX/FFX +update_ffx=0; +update_rfx=0; +if strcmp(assign.init_par,'fixed') + update_ffx=1; +end +if strcmp(assign.init_par,'random') + update_rfx=1; +end +if strcmp(assign.flow_par,'fixed') + update_ffx=1; +end +if strcmp(assign.flow_par,'random') + update_rfx=1; +end +if strcmp(assign.out_par,'fixed') + update_ffx=1; +end +if strcmp(assign.out_par,'random') + update_rfx=1; +end \ No newline at end of file diff --git a/toolbox/mci/inference/spm_mvtpdf.m b/toolbox/mci/inference/spm_mvtpdf.m new file mode 100644 index 00000000..537b7709 --- /dev/null +++ b/toolbox/mci/inference/spm_mvtpdf.m @@ -0,0 +1,30 @@ +function [p] = spm_mvtpdf (x,mu,Lambda,v) +% PDF of multivariate T-distribution +% FORMAT [p] = spm_mvtpdf (x,mu,Lambda,v) +% +% x - ordinates [d x N] +% mu - mean [d x 1] +% Lambda - precision matrix [d x d] +% v - degrees of freedom +% +% p - probability density +% +% See J. Bernardo and A. Smith (2000) +% Bayesian Theory, Wiley (page 435) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_mvtpdf.m 6548 2015-09-11 12:39:47Z will $ + +d=length(mu); +lnz=gammaln(0.5*v)+0.5*d*log(v)+0.5*d*log(pi); +lnz=lnz-0.5*spm_logdet(Lambda)-gammaln(0.5*(v+d)); +z=exp(lnz); + +N=size(x,2); +for n=1:N, + e=x(:,n)-mu; + p(n)=(1+(1/v)*e'*Lambda*e).^(-0.5*(v+d)); +end +p=p/z; diff --git a/toolbox/mci/inference/spm_nwcov.m b/toolbox/mci/inference/spm_nwcov.m new file mode 100644 index 00000000..3cfa958e --- /dev/null +++ b/toolbox/mci/inference/spm_nwcov.m @@ -0,0 +1,34 @@ +function [M] = spm_nwcov (M) +% Get second moments of Normal-Wishart +% FORMAT [M] = spm_nwcov (M) +% +% .mean_prior_cov Prior covariance of mean +% .sample_prior_cov Prior covariance of samples +% .mean_post_cov Posterior covariance of mean +% .sample_pred_cov Predictive covariance of samples +% +% The latter quantity is also the covariance of the predictive density +% The marginal distributions of the mean and of the samples +% are multivariate-T, not Gaussian. +% +% See J. Bernardo and A. Smith (2000) +% Bayesian Theory, Wiley (page 435) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_nwcov.m 6548 2015-09-11 12:39:47Z will $ + +% Get prior covariances +M.mean_prior_cov=M.B0/(M.n0*(M.a0-1)); + +alpha=2*M.a0-M.P+1; +w_s=(1+1/M.n0)/(0.5*alpha-1); +M.sample_prior_cov=w_s*M.B0; + +% Get posterior covariances +M.mean_post_cov=M.BN/(M.nN*(M.aN-1)); + +alpha=2*M.aN-M.P+1; +w_s=(1+1/M.nN)/(0.5*alpha-1); +M.sample_pred_cov=w_s*M.BN; \ No newline at end of file diff --git a/toolbox/mci/inference/spm_nwpost.m b/toolbox/mci/inference/spm_nwpost.m new file mode 100644 index 00000000..2b162d72 --- /dev/null +++ b/toolbox/mci/inference/spm_nwpost.m @@ -0,0 +1,45 @@ +function [M] = spm_nwpost (M,w) +% Get posterior distribution over m,Lambda +% FORMAT [M] = spm_nwpost (M,w) +% +% M M.prior - params of Normal-Wishart prior +% w Multivariate data samples +% +% M M.post - params of Normal-Wishart posterior +% +% Bernardo and Smith, Bayesian Theory, 2000 (p.441) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_nwpost.m 6548 2015-09-11 12:39:47Z will $ + +mw=mean(w,2); +Sw=cov(w',1); + +N=size(w,2); + +prior=M.prior; +P=prior.P; +a0=prior.a; +B0=prior.B; +beta0=prior.beta; +m0=prior.m; + +post.beta=beta0+N; +post.m=(beta0*m0+N*mw)/post.beta; + +post.a=a0+N/2; +post.B=B0+0.5*N*Sw+0.5*(beta0*N/post.beta)*(mw-m0)*(mw-m0)'; +post.a=a0+N/2; + +% Quantities for predictive density (over new samples) +post.mu_w=post.m; +w_s=(post.beta/(post.beta+1))*(post.a-0.5*(P-1)); +post.Lambda_w=w_s*inv(post.B); +post.v_w=2*post.a-P+1; + +% Wrap up +post.P=P; +post.N=N; +M.post=post; \ No newline at end of file diff --git a/toolbox/mci/inference/spm_nwrnd.m b/toolbox/mci/inference/spm_nwrnd.m new file mode 100644 index 00000000..f5c9e41c --- /dev/null +++ b/toolbox/mci/inference/spm_nwrnd.m @@ -0,0 +1,46 @@ +function [m,Lambda,Cm] = spm_nwrnd (M,N) +% Generate N samples from Normal-Wishart density +% FORMAT [m,Lambda,Cm] = spm_nwrnd (M,N) +% +% Parameters M +% .a,.B,.beta,.m +% N number of samples +% +% m Means +% Lambda precisions +% Cm covariances +% +% See J. Bernardo and A. Smith (2000) +% Bayesian Theory, Wiley (page 435) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_nwrnd.m 6548 2015-09-11 12:39:47Z will $ + +%iB=inv(M.B); +for n=1:N, + % Sample precisions + %L=wishrnd(iB,M.a); + L=spm_wishrnd(M.B,M.a); + C=pinv(L); + + if nargout>1 + Lambda(:,:,n)=L; + Cm(:,:,n)=C; + end + + % Sample means + %C=pinv(M.beta*L); + %m(:,n)=spm_normrnd(M.m,C,1); + + m(:,n)=spm_normrnd(M.m,C/M.beta,1); +end + +if N==1 + Lambda=squeeze(Lambda); + Cm=squeeze(Cm); +end + + + diff --git a/toolbox/mci/inference/spm_wishrnd.m b/toolbox/mci/inference/spm_wishrnd.m new file mode 100644 index 00000000..6edc6a81 --- /dev/null +++ b/toolbox/mci/inference/spm_wishrnd.m @@ -0,0 +1,42 @@ +function [S] = spm_wishrnd (B,a,N) +% Generate N samples from Wishart density +% FORMAT [S] = spm_wishrnd (B,a,N) +% +% B,a Wishart params, d=dim(B) +% N Number of samples +% S [d x d x N] sample matrices or [d x d] if N=1 +% +% The Wishart density here, W(S;a,B), is defined as in p. 435 of +% J. Bernardo and A. Smith, Bayesian Theory, Wiley, 2000. +% We have E[S]=aB^{-1} +% +% This definition is different to eg. C. Bishop, +% Pattern Recognition and Machine Learning, Springer, 2006., who +% have W(S;n,V). They are related by n=2a, V=B^{-1}/2. We have E[S]=nV +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: spm_wishrnd.m 6548 2015-09-11 12:39:47Z will $ + +if nargin < 3 | isempty(N) + N=1; +end + +d = size(B,1); +n = 2*a; +V = 0.5*inv(B); + +% Generate scatter matrices i.e. outer product of n Gaussian variates +% with mean 0 and covariance V +x = spm_normrnd(zeros(d,1),V,n*N); +S = zeros(d,d,N); +for s=1:N, + ind = [(s-1)*n+1:s*n]; + S(:,:,s) = x(:,ind)*x(:,ind)'; +end + +if N==1 + S = squeeze(S); +end + \ No newline at end of file diff --git a/toolbox/mci/mci_setpath.m b/toolbox/mci/mci_setpath.m new file mode 100644 index 00000000..7caf9251 --- /dev/null +++ b/toolbox/mci/mci_setpath.m @@ -0,0 +1,23 @@ + +% Add subdirectories to path +addpath(genpath(pwd)); + +% Set work_dir so as to put SPM12 and Sundials on your search path +%work_dir='C:\Users\admin\work\'; +work_dir='D:\home\wpenny\'; + +disp('Adding SPM12 and Sundials to path '); +disp(' '); + +add_dir{1}=[work_dir,'spm12']; +add_dir{2}=[work_dir,'spm12\toolbox\dcm_meeg']; +add_dir{3}=[work_dir,'spm12\toolbox\spectral']; +add_dir{4}=[work_dir,'fil\sampling\sundials-2.5.0\sundialsTB\cvodes']; +add_dir{5}=[work_dir,'fil\sampling\sundials-2.5.0\sundialsTB\cvodes\cvm']; + +for i=1:length(add_dir) + addstr=['addpath ',add_dir{i}]; + disp(addstr); + eval(addstr); +end + diff --git a/toolbox/mci/models/approach/mci_approach_deriv.m b/toolbox/mci/models/approach/mci_approach_deriv.m new file mode 100644 index 00000000..d1bd6b4c --- /dev/null +++ b/toolbox/mci/models/approach/mci_approach_deriv.m @@ -0,0 +1,35 @@ +function [dLdp,iCpY,L] = mci_approach_deriv (P,M,U,Y) +% Gradient of log-likelihood for approach model +% FORMAT [dLdp,iCpY,L] = mci_approach_deriv (P,M,U,Y) +% +% dLdp gradient of log joint +% iCpY curvature (Fisher Information) +% L log joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_approach_deriv.m 6548 2015-09-11 12:39:47Z will $ + +G = mci_approach_gen (P,M,U); +if isstruct(Y) + e = Y.y-G; +else + e = Y-G; +end + +V=exp(P(1)); +tau=exp(P(2)); +t=U.X; + +y=-60+V*(1-exp(-t/tau)); + +dydp = [V*(1-exp(-t/tau)),-V*exp(-t/tau).*(t/tau)]; +dLdp = dydp'*M.iCe*e; +iCpY = dydp'*M.iCe*dydp; + +if nargout > 2 + L = mci_approach_like (P,M,U,Y); +end + + diff --git a/toolbox/mci/models/approach/mci_approach_gen.m b/toolbox/mci/models/approach/mci_approach_gen.m new file mode 100644 index 00000000..671bed91 --- /dev/null +++ b/toolbox/mci/models/approach/mci_approach_gen.m @@ -0,0 +1,18 @@ +function [y] = mci_approach_gen (P,M,U) +% Approach to limit model +% FORMAT [y] = mci_approach_gen (P,M,U) +% +% P parameters +% M,U as usual +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_approach_gen.m 6548 2015-09-11 12:39:47Z will $ + +V=exp(P(1)); +tau=exp(P(2)); +t=U.X; + +y=-60+V*(1-exp(-t/tau)); + diff --git a/toolbox/mci/models/approach/mci_approach_like.m b/toolbox/mci/models/approach/mci_approach_like.m new file mode 100644 index 00000000..ab9f6438 --- /dev/null +++ b/toolbox/mci/models/approach/mci_approach_like.m @@ -0,0 +1,22 @@ +function [L,yhat,st] = mci_approach_like (P,M,U,Y) +% Log-likelihood for approach model +% FORMAT [L,yhat,st] = mci_approach_like (P,M,U,Y) +% +% P parameters +% M,U,Y as usual +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_approach_like.m 6548 2015-09-11 12:39:47Z will $ + +% Status flag (only used for dynamic systems) +st=[]; + +T=length(Y); +yhat = mci_approach_gen (P,M,U); +E=sum(sum((Y-yhat).^2)); + +L = -0.5*T*M.logdet_Ce - 0.5*T*log(2*pi); +L = L - 0.5*M.iCe*E; + diff --git a/toolbox/mci/models/approach/mci_approach_struct.m b/toolbox/mci/models/approach/mci_approach_struct.m new file mode 100644 index 00000000..36b3a722 --- /dev/null +++ b/toolbox/mci/models/approach/mci_approach_struct.m @@ -0,0 +1,35 @@ +function [M,U] = mci_approach_struct (Nobs) +% Approach model structure +% FORMAT [M,U] = mci_approach_struct (Nobs) +% +% Nobs Number of observations +% M Model structure +% U Input structure +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_approach_struct.m 6548 2015-09-11 12:39:47Z will $ + +M.l=1; % Single output variable + +M.T=40; +dt=M.T/(Nobs-1); +U.X=[0:dt:M.T]'; +M.N=length(U.X); +M.t=U.X; + +M.L='mci_approach_like'; +M.IS='mci_approach_gen'; +M.dL='mci_approach_deriv'; + +M.pE=[log(20),log(5)]'; +M.pC=[1/16 0; 0 1/16]; + +sigma_e=1; +M.Ce=sigma_e^2; +M.logdet_Ce=spm_logdet(M.Ce); +M.iCe=inv(M.Ce); + + + diff --git a/toolbox/mci/models/discount/mci_discount_act.m b/toolbox/mci/models/discount/mci_discount_act.m new file mode 100644 index 00000000..b89440e4 --- /dev/null +++ b/toolbox/mci/models/discount/mci_discount_act.m @@ -0,0 +1,22 @@ +function [a,v1,v2,k] = mci_discount_act (P,M,U) +% Activation of discounting model +% FORMAT [a,v1,v2,k] = mci_discount_act (P,M,U) +% +% P parameters +% M model structure +% U contains rewards and times +% +% a activation for discount model +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_discount_act.m 6548 2015-09-11 12:39:47Z will $ + +k = exp(P(1)); % discount rate +beta = exp(P(2)); % decision noise + +v1 = U.r1./(1+k*U.t1); +v2 = U.r2./(1+k*U.t2); + +a = beta * (v1 - v2); \ No newline at end of file diff --git a/toolbox/mci/models/discount/mci_discount_deriv.m b/toolbox/mci/models/discount/mci_discount_deriv.m new file mode 100644 index 00000000..830e951e --- /dev/null +++ b/toolbox/mci/models/discount/mci_discount_deriv.m @@ -0,0 +1,29 @@ +function [dLdp,iCpY,L] = mci_discount_deriv (P,M,U,Y) +% Gradient of likelihood for discounting model +% FORMAT [dLdp,iCpY,L] = mci_discount_deriv (P,M,U,Y) +% +% P parameters +% M model structure +% U contains rewards and times +% Y data +% +% dLdp gradient of log joint +% iCpY curvature (Fisher Information) +% L log joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_discount_deriv.m 6548 2015-09-11 12:39:47Z will $ + +dLdp = spm_diff(M.L,P,M,U,Y,1); +dLdp = full(dLdp(:)); + +X = spm_diff('mci_discount_act',P,M,U,1); +g = mci_discount_gen (P,M,U); +Lambda=diag(g.*(1-g)); +iCpY=X'*Lambda*X; + +if nargout > 2 + L=mci_discount_like (P,M,U,Y); +end \ No newline at end of file diff --git a/toolbox/mci/models/discount/mci_discount_gen.m b/toolbox/mci/models/discount/mci_discount_gen.m new file mode 100644 index 00000000..ecbebef8 --- /dev/null +++ b/toolbox/mci/models/discount/mci_discount_gen.m @@ -0,0 +1,20 @@ +function [g,y] = mci_discount_gen (P,M,U) +% Output of discounting model +% FORMAT [g,y] = mci_discount_gen (P,M,U) +% +% P parameters +% M model structure +% U U.X contains design matrix +% +% g probability of taking option 1 +% y binary decisions based on g +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_discount_gen.m 6548 2015-09-11 12:39:47Z will $ + +a = mci_discount_act (P,M,U); +g = 1./(1+exp(-a)); + +y = g > rand(M.N,1); \ No newline at end of file diff --git a/toolbox/mci/models/discount/mci_discount_like.m b/toolbox/mci/models/discount/mci_discount_like.m new file mode 100644 index 00000000..be89f76d --- /dev/null +++ b/toolbox/mci/models/discount/mci_discount_like.m @@ -0,0 +1,37 @@ +function [L,E,st] = mci_discount_like (P,M,U,Y) +% Compute log likelihood of discount model +% FORMAT [L,E,st] = mci_discount_like (P,M,U,Y) +% +% P parameters +% M model +% U inputs +% Y data +% +% L Log likelihood +% E Errors +% st Status flag (0 for OK, -1 for problem) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_discount_like.m 6548 2015-09-11 12:39:47Z will $ + +st=0; + +g = mci_discount_gen (P,M,U); + +T=length(g); +if isstruct(Y) + y=Y.y; +else + y=Y; +end + +L=0; +for n=1:T, + L = L + y(n)*log(g(n)+eps)+(1-y(n))*log(1-g(n)+eps); +end +E=-L; + + + diff --git a/toolbox/mci/models/discount/mci_discount_struct.m b/toolbox/mci/models/discount/mci_discount_struct.m new file mode 100644 index 00000000..282e799f --- /dev/null +++ b/toolbox/mci/models/discount/mci_discount_struct.m @@ -0,0 +1,39 @@ +function [M,U] = mci_discount_struct (Nobs) +% Set up data structures for discounting model +% FORMAT [M,U] = mci_discount_struct (Nobs) +% +% Nobs number of data points +% +% M model structure +% U U.X is the design matrix +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_discount_struct.m 6548 2015-09-11 12:39:47Z will $ + +% Number of data points +try T=Nobs; catch T=100; end + +% Set rewards +rmax=70; % maximum reward (pounds) +U.r1=floor(rand(T,1)*24+16); % low +U.r2=floor(U.r1+rand(T,1).*(rmax-U.r1)); % high + +% Set delays +tmax=75; % maximum delay (weeks) +U.t1=floor(rand(T,1)*54+1); % short +U.t2=floor(U.t1+rand(T,1).*(tmax-U.t1)); % long + +M.L='mci_discount_like'; +M.dL='mci_discount_deriv'; +M.IS='mci_discount_gen'; + +Np=2; +M.pE=zeros(Np,1); +M.pC=diag([1 1]); +M.l=1; +M.T=T; +M.N=T; + + diff --git a/toolbox/mci/models/growth/mci_pb_deriv.m b/toolbox/mci/models/growth/mci_pb_deriv.m new file mode 100644 index 00000000..eb3f45c0 --- /dev/null +++ b/toolbox/mci/models/growth/mci_pb_deriv.m @@ -0,0 +1,35 @@ +function [dLdp,iCpY,L] = mci_pb_deriv (P,M,U,Y) +% Gradient of log-likelihood for Preece-Baines model +% FORMAT [dLdp,iCpY,L] = mci_pb_deriv (P,M,U,Y) +% +% dLdp gradient of log joint +% iCpY curvature (Fisher Information) +% L log joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_pb_deriv.m 6548 2015-09-11 12:39:47Z will $ + +dydp = spm_diff(M.IS,P,M,U,1); + +G = mci_pb_gen (P,M,U); +if isstruct(Y) + e = Y.y-G; +else + e = Y-G; +end + +N=length(e); +iCe=M.iCe*eye(N); + +dLdp = dydp'*iCe*e; +%dLdp = spm_diff(M.L,P,M,U,Y,1); + +iCpY = dydp'*iCe*dydp; + +if nargout > 2 + L = mci_pb_like (P,M,U,Y); +end + + diff --git a/toolbox/mci/models/growth/mci_pb_gen.m b/toolbox/mci/models/growth/mci_pb_gen.m new file mode 100644 index 00000000..1427a598 --- /dev/null +++ b/toolbox/mci/models/growth/mci_pb_gen.m @@ -0,0 +1,28 @@ +function [y] = mci_pb_gen (P,M,U) +% Preece-Baines growth model +% FORMAT [y] = mci_pb_gen (P,M,U) +% +% P parameters +% M model +% U inputs +% +% y time series +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_pb_gen.m 6548 2015-09-11 12:39:47Z will $ + +t=U.X; + +s0=exp(P(1)); % Pre-pubertal velocity +s1=exp(P(2)); % Post-pubertal velocity +t0=P(3); % Age at growth spurt + +h1=P(4); % asymptotic height +h0=P(5); % height at growth spurt + +e1=exp(s0*(t-t0)); +e2=exp(s1*(t-t0)); + +y=h1-2*(h1-h0)./(e1+e2); \ No newline at end of file diff --git a/toolbox/mci/models/growth/mci_pb_like.m b/toolbox/mci/models/growth/mci_pb_like.m new file mode 100644 index 00000000..1c69303a --- /dev/null +++ b/toolbox/mci/models/growth/mci_pb_like.m @@ -0,0 +1,22 @@ +function [L,yhat,st] = mci_pb_like (P,M,U,Y) +% Log-likelihood for Preece-Baines model +% FORMAT [L,yhat,st] = mci_pb_like (P,M,U,Y) +% +% P parameters +% M,U,Y as usual +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_pb_like.m 6548 2015-09-11 12:39:47Z will $ + +% Status flag (only used for dynamic systems) +st=[]; + +T=length(Y); +yhat = mci_pb_gen (P,M,U); +E=sum(sum((Y-yhat).^2)); + +L = M.logdet_Ce - 0.5*T*log(2*pi); +L = L - 0.5*M.iCe*E; + diff --git a/toolbox/mci/models/growth/mci_pb_struct.m b/toolbox/mci/models/growth/mci_pb_struct.m new file mode 100644 index 00000000..b76a2453 --- /dev/null +++ b/toolbox/mci/models/growth/mci_pb_struct.m @@ -0,0 +1,36 @@ +function [M,U] = mci_pb_struct (Nobs) +% Preece-Baines model structure +% FORMAT [M,U] = mci_pb_struct (Nobs) +% +% Nobs Number of observations +% +% M Model structure +% U Input structure +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_pb_struct.m 6548 2015-09-11 12:39:47Z will $ + +M.l=1; % Single output variable + +M.T=30; +dt=M.T/(Nobs-1); +U.X=[0:dt:M.T]'; +M.N=length(U.X); +M.t=U.X; + +M.L='mci_pb_like'; +M.IS='mci_pb_gen'; +M.dL='mci_pb_deriv'; + +M.pE=[log(0.02),log(0.4),11,90,60]'; +M.pC=diag(abs(M.pE/10)); + +sigma_e=1; +M.Ce=sigma_e^2; +M.logdet_Ce=spm_logdet(M.Ce); +M.iCe=inv(M.Ce); + + + diff --git a/toolbox/mci/models/lds/mci_exp_init.m b/toolbox/mci/models/lds/mci_exp_init.m new file mode 100644 index 00000000..c38c70dc --- /dev/null +++ b/toolbox/mci/models/lds/mci_exp_init.m @@ -0,0 +1,47 @@ +function [w0,a] = mci_exp_init (Y,M,doplot) +% Exponentially interpolate to t=0 +% FORMAT [w0,a] = mci_exp_init (Y,M,doplot) +% +% Y Cell of data from multiple subjects +% Y{n}.y, Y{n}.ind for n=1..N +% M Model structure +% doplot plot fits +% +% w0 [d x N] matrix of initial states +% where d is number of states +% a [d x N] matrix of exponential coefficients +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_exp_init.m 6548 2015-09-11 12:39:47Z will $ + +if nargin < 3 + doplot=0; +end + +N=length(Y); +d=size(Y{1}.y,2); + +for n=1:N, + for j=1:d, + % Fit + keep=find(Y{n}.y(:,j)>0); + ydat=Y{n}.y(keep,j); + ind=Y{n}.ind(keep); + Nt=size(ydat,1); + xd=[ind(:)*M.dt,ones(Nt,1)]; + beta=pinv(xd)*log(ydat); + + % Extrapolate + y0=exp(beta(2)); + yhat=y0*exp(beta(1)*M.t); + + if doplot + figure;plot(ind*M.dt,ydat,'x'); + hold on; plot(M.t,yhat,'r'); + end + w0(j,n)=y0; + a(j,n)=beta(1); + end +end \ No newline at end of file diff --git a/toolbox/mci/models/lds/mci_interp_init.m b/toolbox/mci/models/lds/mci_interp_init.m new file mode 100644 index 00000000..98bc0dc4 --- /dev/null +++ b/toolbox/mci/models/lds/mci_interp_init.m @@ -0,0 +1,40 @@ +function [w0] = mci_interp_init (Y,M) +% Linear interpolate to t=0 +% FORMAT [w0] = mci_interp_init (Y,M) +% +% Y Cell of data from multiple subjects +% Y{n}.y, Y{n}.ind for n=1..N +% M Model structure +% +% w0 [d x N] matrix of initial states +% where d is number of states +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_interp_init.m 6548 2015-09-11 12:39:47Z will $ + +N=length(Y); +d=size(Y{1}.y,2); +doplot=0; + +for n=1:N, + for j=1:d, + + % Fit + Nt=size(Y{n}.y,1); + xd=[Y{n}.ind(:),ones(Nt,1)]; + yd=Y{n}.y(:,j); + beta=pinv(xd)*yd; + + % Extrapolate + xt=[[1:M.N]',ones(M.N,1)]; + yhat=xt*beta; + + if doplot + figure;plot(Y{n}.ind,yd,'x'); + hold on; plot([1:M.N]',yhat,'r'); + end + w0(j,n)=yhat(1); + end +end \ No newline at end of file diff --git a/toolbox/mci/models/lds/mci_lds_dfdx.m b/toolbox/mci/models/lds/mci_lds_dfdx.m new file mode 100644 index 00000000..94f46068 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_dfdx.m @@ -0,0 +1,27 @@ +function [A,Pt] = mci_lds_dfdx (x,u,P,M) +% Jacobian for linear system, dx/dt=Ax, with constrained connectivity +% FORMAT [A,Pt] = mci_lds_dfdx (x,u,P,M) +% +% x State vector +% u input +% P parameters (vectorised) +% M model structure +% +% A f=Ax +% Pt Parameters (transformed from latent pars) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_dfdx.m 6548 2015-09-11 12:39:47Z will $ + +[Pt,a,b] = mci_lds_lat2par (P,M); +A=diag(a); + +Nb=length(b); +for k=1:Nb, + i=M.Aconn(k,1); + j=M.Aconn(k,2); + A(i,j)=b(k); +end + diff --git a/toolbox/mci/models/lds/mci_lds_fx.m b/toolbox/mci/models/lds/mci_lds_fx.m new file mode 100644 index 00000000..f32943a4 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_fx.m @@ -0,0 +1,20 @@ +function [f,A,Pt] = mci_lds_fx (x,u,P,M) +% Flow for linear system, dx/dt=Ax, with constrained connectivity +% FORMAT [f,A,Pt] = mci_lds_fx (x,u,P,M) +% +% x State vector +% u input +% P parameters (vectorised) +% M model structure +% +% f Flow, dx/dt +% A f=Ax +% Pt Parameters (transformed from latent pars) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_fx.m 6548 2015-09-11 12:39:47Z will $ + +[A,Pt] = mci_lds_dfdx (x,u,P,M); +f=A*x; \ No newline at end of file diff --git a/toolbox/mci/models/lds/mci_lds_gen.m b/toolbox/mci/models/lds/mci_lds_gen.m new file mode 100644 index 00000000..3e3f7208 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_gen.m @@ -0,0 +1,19 @@ +function [Y] = mci_lds_gen (M,U,P) +% LDS constrained: generate data +% FORMAT [Y] = mci_lds_gen (M,U,P) +% +% M Model structure +% U Inputs +% P Parameters +% +% Y Data +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_gen.m 6548 2015-09-11 12:39:47Z will $ + +G = spm_mci_fwd (P,M,U); +e = M.sd*randn(M.N,M.d); +Y = G+e; + diff --git a/toolbox/mci/models/lds/mci_lds_group_data.m b/toolbox/mci/models/lds/mci_lds_group_data.m new file mode 100644 index 00000000..3749391d --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_group_data.m @@ -0,0 +1,113 @@ +function [pinit,pflow,names,M,U,Y] = mci_lds_group_data (lds) +% Generate LDS data for a group of subjects +% FORMAT [pinit,pflow,names,M,U,Y] = mci_lds_group_data (lds) +% +% lds Data structure with fields: +% +% .R R.pE, R.pC prior over initial conds +% .sd Standard deviation of observation noise +% .Nsub Number of subjects +% .Nobs Number of observations per subject +% .model 'lds_real','forward',etc. +% .flow_par 'fixed' or 'random' +% .init_par 'fixed' or 'random' +% +% pinit Initial params +% pflow Flow params +% names names of parameters +% M Cell of models +% U Cell of inputs +% Y Cell of data +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_group_data.m 6548 2015-09-11 12:39:47Z will $ + +R=lds.R; sd=lds.sd; Nsub=lds.Nsub; +Nobs=lds.Nobs; model=lds.model; +d=length(R.pE); + +% Set Flow Parameters +if strcmp(model,'lds_real') + r=linspace(0.3,0.9,d); + T=100; + q=(1/T)*log(r); + pflow=log(-q); +else + + Mtmp.name=model; + Mtmp.sd=sd; + Mtmp.d=d; + Mtmp.drop=0.5; + %Mtmp.t=[1:400]'/4; + Mtmp.t=[1:25]'/0.25; + Mtmp.int='sundials'; + + Mtmp=mci_lds_struct(Mtmp); + if strcmp(lds.flow_par,'fixed') +% PP.self=Mtmp.sd_self*randn(d,1); +% PP.between=Mtmp.sd_between*randn(Mtmp.Nb,1); +% pflow=spm_vec(PP); + + Pt = [-0.04,-0.01,-0.005,-0.01,0.01,0.01,0.01]'; + pflow = mci_lds_par2lat (Pt,Mtmp); + else + for n=1:Nsub, + PP.self=Mtmp.sd_self*randn(d,1); + PP.between=Mtmp.sd_between*randn(Mtmp.Nb,1); + pflow(:,n)=spm_vec(PP); + end + end +end + +if strcmp(lds.init_par,'fixed') + R0 = spm_normrnd(R.pE,R.pC,1); +end +for n=1:Nsub, + + % Sample initial states from prior + if strcmp(lds.init_par,'random') + R0 = spm_normrnd(R.pE,R.pC,1); + end + pinit(:,n)=R0; + switch model + case 'lds_real' + [M{n},U{n},y] = irlds_init (d,sd,R0,pflow); + otherwise + Mtmp.R=R0; + [M{n},U{n}] = mci_lds_struct (Mtmp); + + if strcmp(lds.flow_par,'fixed') + y = mci_lds_gen (M{n},U{n},pflow); + else + y = mci_lds_gen (M{n},U{n},pflow(:,n)); + end + end + + if lds.Nobs==M{n}.N + Y{n}.y=y; + Y{n}.ind=1:M{n}.N; + else + % Thin observations to selected time points + rind=randperm(M{n}.N); + ind=rind(1:Nobs); + Y{n}.y=y(ind,:); + Y{n}.ind=ind; + end + +end + +if strcmp(model,'forward') + + for j=1:d, + jn=int2str(j); + names{j}=['a_{',jn,jn,'}']; + end + for i=1:d-1, + jn=int2str(i); + j1n=int2str(i+1); + names{i+j}=['a_{',j1n,jn,'}']; + end + +end diff --git a/toolbox/mci/models/lds/mci_lds_gx.m b/toolbox/mci/models/lds/mci_lds_gx.m new file mode 100644 index 00000000..7c6b7b43 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_gx.m @@ -0,0 +1,11 @@ +function [y,L] = mci_lds_gx (x,u,P,M) +% Observation function for LDS +% FORMAT [y,L] = mci_lds_gx (x,u,P,M) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_gx.m 6548 2015-09-11 12:39:47Z will $ + +L=eye(M.n); +y=L*x; \ No newline at end of file diff --git a/toolbox/mci/models/lds/mci_lds_lat2par.m b/toolbox/mci/models/lds/mci_lds_lat2par.m new file mode 100644 index 00000000..93752665 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_lat2par.m @@ -0,0 +1,24 @@ +function [Pt,a,b] = mci_lds_lat2par (P,M) +% Convert latent params to params +% FORMAT [Pt,a,b] = mci_lds_lat2par (P,M) +% +% P Parameters (latent) +% M model structure +% +% Pt Parameters (transformed) +% a diagonal values +% b off-diagonal values +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_lat2par.m 6548 2015-09-11 12:39:47Z will $ + +% Diagonal entries +s=exp(P(1:M.d)); +a=s*M.a_typical; + +% Off-diagonal entries +b=P(M.d+1:end); + +Pt=[a(:);b(:)]; \ No newline at end of file diff --git a/toolbox/mci/models/lds/mci_lds_par2lat.m b/toolbox/mci/models/lds/mci_lds_par2lat.m new file mode 100644 index 00000000..f1f8f464 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_par2lat.m @@ -0,0 +1,19 @@ +function [P] = mci_lds_par2lat (Pt,M) +% Convert parmas to latent params +% FORMAT [P] = mci_lds_par2lat (Pt,M) +% +% Pt params +% M model struct +% +% P params (latent) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_par2lat.m 6548 2015-09-11 12:39:47Z will $ + +a=Pt(1:M.d)/M.a_typical; +a=log(a); +b=Pt(M.d+1:end); + +P=[a(:);b(:)]; \ No newline at end of file diff --git a/toolbox/mci/models/lds/mci_lds_params.m b/toolbox/mci/models/lds/mci_lds_params.m new file mode 100644 index 00000000..09c1ba22 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_params.m @@ -0,0 +1,15 @@ +function [P] = mci_lds_params (M,U) +% LDS constrained: sample params from prior +% FORMAT [P] = mci_lds_params (M,U) +% +% M Model structure +% U Inputs +% +% P Parameters +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_params.m 6548 2015-09-11 12:39:47Z will $ + +P=spm_normrnd(M.pE,M.pC,1); diff --git a/toolbox/mci/models/lds/mci_lds_plot_fit.m b/toolbox/mci/models/lds/mci_lds_plot_fit.m new file mode 100644 index 00000000..6aa739d7 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_plot_fit.m @@ -0,0 +1,93 @@ +function [] = mci_lds_plot_fit (MCI,lds,n,plotfit) +% Plot fit from group LDS estimation +% FORMAT [] = mci_lds_plot_fit (MCI,lds,n,plotfit) +% +% MCI MCI-MFX data structure +% lds true model data structure with fields: +% +% .pinit true init params +% .pflow true flow params +% n subject number +% plotfit 1 to plot model fit, 0 otherwise (default) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_plot_fit.m 6548 2015-09-11 12:39:47Z will $ + +if nargin < 4 + plotfit=0; +end + +M=MCI.M; U=MCI.U; Y=MCI.Y; + +d=size(MCI.M{1}.x0,1); + +% True model responses and data +switch lds.init_par, + case 'fixed', + R0 = lds.pinit; + case 'random', + R0 = lds.pinit(:,n); + otherwise + % Assume known + R0 = M{n}.x0; +end +if strcmp(lds.flow_par,'fixed') + Pf = lds.pflow; +else + Pf = lds.pflow(:,n); +end +M{n}.x0=R0; +y = spm_mci_fwd (Pf,M{n},U{n}); + +% Model fits +if plotfit + switch MCI.assign.init_par, + case 'fixed', + R0 = MCI.pinit(:); + case 'random', + R0 = MCI.pinit_sub(:,n); + otherwise + % Assume known + R0 = M{n}.x0; + end + if strcmp(MCI.assign.flow_par,'fixed') + Pf = MCI.pflow(:); + else + Pf = MCI.pflow_sub(:,n); + end + M{n}.x0=R0; + yhat = spm_mci_fwd (Pf,M{n},U{n}); +end + + +% Data +try ind=Y{n}.ind; catch ind=1:M{n}.N; end +Ny=size(y,2); +ry=ceil(sqrt(Ny)); + +% Plotting +lw=2; +h=figure; +set(h,'Name',sprintf('Subject %d',n)); +for j=1:Ny, + subplot(ry,ry,j); + plot(M{n}.t,y(:,j),'k','LineWidth',lw); + hold on + plot(M{n}.t(ind),Y{n}.y(:,j),'kx','LineWidth',lw,'MarkerSize',12); + if plotfit + plot(M{n}.t,yhat(:,j),'r','LineWidth',lw); + end + %ylim([0 5]); + grid on + % if j < Ny-ry + % set(gca,'XTickLabel',[]); + % end + + set(gca,'FontSize',16); + ylabel(['x_',int2str(j)]); + if j > 2 + xlabel('Month'); + end +end diff --git a/toolbox/mci/models/lds/mci_lds_plot_params.m b/toolbox/mci/models/lds/mci_lds_plot_params.m new file mode 100644 index 00000000..763bb33f --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_plot_params.m @@ -0,0 +1,102 @@ +function [rmse] = mci_lds_plot_results (MCI,lds) +% Plot results of group LDS estimation +% FORMAT [rmse] = mci_lds_plot_results (MCI,lds) +% +% MCI MCI-MFX data structure +% lds true model data structure with fields: +% +% .pinit true init params +% .pflow true flow params +% +% rmse root mean square errors +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_plot_params.m 6548 2015-09-11 12:39:47Z will $ + +pinit=lds.pinit; pflow=lds.pflow; +d=size(MCI.M{1}.x0,1); +Nsub=length(MCI.Y); + +% Use mean or median when computing RMSE +use_median=0; + +% Initial state estimates +for j=1:d, + h=figure; + set(h,'Name',sprintf('Initial State %d',j)); + + min_w=min(pinit(j,:)); + max_w=max(pinit(j,:)); + + if strcmp(MCI.assign.init_par,'random') + hold on + plot(pinit(j,:),MCI.pinit_sub(j,:),'kx','MarkerSize',10); + e2=MCI.pinit_sub-pinit; + else + hold on + plot(pinit(j,:),MCI.pinit(j),'kx','MarkerSize',10); + e2=MCI.pinit(:)*ones(1,Nsub)-pinit; + end + plot([min_w max_w],[min_w max_w],'k-','LineWidth',2); + set(gca,'FontSize',18); + xlabel('True'); + ylabel('Estimated'); + grid on + +end + +if use_median + rmse.pinit=sqrt(median(diag(e2'*e2))/d); +else + rmse.pinit=sqrt(mean(diag(e2'*e2))/d); +end + +disp(' '); +disp('Initial state parameters:'); +disp(sprintf('Pinit RMSE=%1.2f',rmse.pinit)); + + + +% Flow estimates +h=figure; +set(h,'Name','Flow Parameters'); +min_v=min(pflow); +max_v=max(pflow); +plot([min_v max_v],[min_v max_v],'k-','LineWidth',2); +hold on +if strcmp(MCI.assign.flow_par,'random') + if strcmp(lds.flow_par,'fixed') + e2=MCI.pflow-pflow*ones(1,Nsub); + for n=1:Nsub, + plot(pflow,MCI.pflow(:,n),'kx','MarkerSize',10); + end + else + e2=MCI.pflowK-pflow; + for n=1:Nsub, + plot(pflow(:,n),MCI.pflow(:,n),'kx','MarkerSize',10); + end + end +else + plot(pflow,MCI.pflow,'kx','MarkerSize',10); + if strcmp(lds.flow_par,'fixed') + e2=MCI.pflow-pflow; + else + e2=MCI.pflow-pflow*ones(1,Nsub); + end +end +set(gca,'FontSize',18); +xlabel('True'); +ylabel('Estimated'); + +Np=length(pflow); +if use_median + rmse.pflow=sqrt(median(diag(e2'*e2))/Np); +else + rmse.pflow=sqrt(mean(diag(e2'*e2))/Np); +end + +disp(' '); +disp('Flow parameters:'); +disp(sprintf('Pflow RMSE=%1.2f',rmse.pflow)); \ No newline at end of file diff --git a/toolbox/mci/models/lds/mci_lds_struct.m b/toolbox/mci/models/lds/mci_lds_struct.m new file mode 100644 index 00000000..e1e13991 --- /dev/null +++ b/toolbox/mci/models/lds/mci_lds_struct.m @@ -0,0 +1,124 @@ +function [M,U,names] = mci_lds_struct (M) +% LDS constrained: Initialise model structure +% FORMAT [M,U,names] = mci_lds_struct (M) +% +% M.d Number of regions +% M.sd Observation noise SD +% M.name 'uncoupled','forward','backward','bidirectional' +% M.R Initial state +% M.t Vector of Times +% M.drop final value as proportion of initial value +% eg. 0.5 indicates typical state at M.t(end) is +% half of M.t(1). Used to set M.a_typical, typical +% self connection values +% +% M Model structure +% U Inputs +% names Names of variables +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_lds_struct.m 6548 2015-09-11 12:39:47Z will $ + +d=M.d; +try R=M.R; catch R=ones(d,1); end + +M.N=length(M.t); +M.T=M.t(end); +M.dt=M.t(2)-M.t(1); + +switch M.name + case 'uncoupled', + disp('No coupling among regions'); + Aconn=[]; + case 'forward', + for i=1:d-1, + Aconn(i,:)=[i+1,i]; + end + case 'backward', + for i=1:d-1, + Aconn(i,:)=[i,i+1]; + end + case 'bidirectional', + % Forward + for i=1:d-1, + Aconn(i,:)=[i+1,i]; + end + % Backward + for i=1:d-1, + Aconn(d+i-1,:)=[i,i+1]; + end + otherwise + disp('Unknown Hypothesis'); +end + +% Number of connections between regions +M.Nb=size(Aconn,1); +M.Aconn=Aconn; + +M.f='mci_lds_fx'; +M.dfdx='mci_lds_dfdx'; +M.g='mci_lds_gx'; + +% Initial states +M.x0=R; +M.x=zeros(d,1); +M.m=0; +M.n=d; +M.l=d; + +% Typical diagonal entry in A matrix +if ~isfield(M,'drop') + M.drop=0.5; +end +M.a_typical=log(M.drop)/M.T; + +% Self connections +M.pEs.self=zeros(d,1); + +% Strength of priors +%M.sd_self=1/4; +%M.sd_between=exp(-5); +M.sd_self=1; +M.sd_between=0.05; + +% Coupling among regions +if M.Nb>0 + M.pEs.between=zeros(M.Nb,1); + M.pC=diag([M.sd_self^2*ones(d,1);M.sd_between^2*ones(M.Nb,1)]); +else + M.pC=M.sd_self^2*eye(d); +end + +M.pE=spm_vec(M.pEs); +M.Npflow=length(M.pE); +M.Npout=0; +M.ipC=inv(M.pC); + +%M.int='euler'; +%M.int='ode15'; +M.int='sundials'; + +% Inputs +U=zeros(1,M.N); +M.m=1; + +% Likelihood function +M.L='spm_mci_glike'; +M.Ce=M.sd^2*eye(d); + +if strcmp(M.name,'forward') + + for j=1:d, + jn=int2str(j); + names{j}=['a_{',jn,jn,'}']; + end + for i=1:d-1, + jn=int2str(i); + j1n=int2str(i+1); + names{i+j}=['a_{',j1n,jn,'}']; + end + +end + diff --git a/toolbox/mci/models/linear/mci_linear_deriv.m b/toolbox/mci/models/linear/mci_linear_deriv.m new file mode 100644 index 00000000..f52428a3 --- /dev/null +++ b/toolbox/mci/models/linear/mci_linear_deriv.m @@ -0,0 +1,34 @@ +function [dLdp,iCpY,L] = mci_linear_deriv (P,M,U,Y) +% Gradient of likelihood for linear regression +% FORMAT [dLdp,iCpY,L] = mci_linear_deriv (P,M,U,Y) +% +% P parameters +% M model +% U inputs +% Y data +% +% dLdp gradient of log joint +% iCpY curvature (Fisher Information) +% L log joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linear_deriv.m 6548 2015-09-11 12:39:47Z will $ + +G = mci_linear_gen (P,M,U); +if isstruct(Y) + e = Y.y-G; +else + e = Y-G; +end +X = U.X; + +dLdp=X'*M.iCe*e; +dLdp=dLdp'; + +iCpY=X'*M.iCe*X; + +if nargout > 2 + L=mci_linear_like (P,M,U,Y); +end \ No newline at end of file diff --git a/toolbox/mci/models/linear/mci_linear_gen.m b/toolbox/mci/models/linear/mci_linear_gen.m new file mode 100644 index 00000000..3094bfb0 --- /dev/null +++ b/toolbox/mci/models/linear/mci_linear_gen.m @@ -0,0 +1,16 @@ +function [y] = mci_linear_gen (theta,M,U) +% Output of linear model +% FORMAT [y] = mci_linear_gen (theta,M,U) +% +% theta regression coefficients +% M model structure +% U U.X contains design matrix +% +% y outputs +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linear_gen.m 6548 2015-09-11 12:39:47Z will $ + +y=U.X*theta(:); diff --git a/toolbox/mci/models/linear/mci_linear_like.m b/toolbox/mci/models/linear/mci_linear_like.m new file mode 100644 index 00000000..42ac00cb --- /dev/null +++ b/toolbox/mci/models/linear/mci_linear_like.m @@ -0,0 +1,30 @@ +function [L,E,st] = mci_linear_like (theta,M,U,Y) +% Compute log likelihood of linear model +% FORMAT [L,E,st] = mci_linear_like (theta,M,U,Y) +% +% theta regression coefficients +% M model +% U inputs +% Y data +% +% L Log likelihood +% E Errors +% st Status flag (0 for OK, -1 for problem) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linear_like.m 6548 2015-09-11 12:39:47Z will $ + +st=0; + +yhat=U.X*theta(:); +T=length(yhat); +if isstruct(Y) + E=sum(sum((Y.y-yhat).^2)); +else + E=sum(sum((Y-yhat).^2)); +end + +L = -0.5*T*M.logdet_Ce - 0.5*T*log(2*pi); +L = L - 0.5*M.iCe*E; diff --git a/toolbox/mci/models/linear/mci_linear_plot_fit.m b/toolbox/mci/models/linear/mci_linear_plot_fit.m new file mode 100644 index 00000000..e91fe10e --- /dev/null +++ b/toolbox/mci/models/linear/mci_linear_plot_fit.m @@ -0,0 +1,25 @@ +function [] = mci_linear_plot_fit (M,Y,y,names) +% Plot fit of linear time series model +% FORMAT [] = mci_linear_plot_fit (M,Y,y,names) +% +% M data structure for linear model +% Y data points +% y{i} ith time series +% names{i} name of +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linear_plot_fit.m 6548 2015-09-11 12:39:47Z will $ + +lw=2; +cols={'k-','b-','r-'}; +hold on +for i=1:length(y) + plot([1:M.T],y{i},cols{i},'LineWidth',lw); +end +plot(M.t,Y,'kx','LineWidth',lw,'MarkerSize',12); +grid on +set(gca,'FontSize',16); +xlabel('Month'); +legend(names); \ No newline at end of file diff --git a/toolbox/mci/models/linear/mci_linear_post.m b/toolbox/mci/models/linear/mci_linear_post.m new file mode 100644 index 00000000..14e4ee74 --- /dev/null +++ b/toolbox/mci/models/linear/mci_linear_post.m @@ -0,0 +1,50 @@ +function [Ep,Cp,L] = mci_linear_post (M,U,Y) +% Analytic posterior for linear regression +% FORMAT [Ep,Cp,L] = mci_linear_post (M,U,Y) +% +% M Model Structure +% U Inputs +% Y Data +% +% Ep Posterior mean +% Cp Posterior covariance +% L Log evidence +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linear_post.m 6548 2015-09-11 12:39:47Z will $ + +ipC=inv(M.pC); +iCe=inv(M.Ce); +X=U.X; +iCp=X'*iCe*X+ipC; +Cp=inv(iCp); +Ep=Cp*(X'*iCe*Y+ipC*M.pE); + +% Log evidence +yhat=X*Ep; +T=length(yhat); +if isstruct(Y) + ey=Y.y-yhat; +else + ey=Y-yhat; +end + +L = -0.5*T*spm_logdet(M.Ce) - 0.5*T*log(2*pi); +L = L - 0.5*trace(ey'*iCe*ey); + +ew = M.pE-Ep; +L = L - 0.5*ew'*ipC*ew - 0.5*spm_logdet(M.pC) + 0.5*spm_logdet(Cp); + + +% Model evidence +% e1=y-glm.yhat; +% e2=glm.mu-glm.Ep; +% accuracy=-0.5*N*log(glm.c1)-0.5*(e1'*e1)/glm.c1; +% complexity=0.5*p*log(glm.c2)-0.5*log(det(glm.Cp))+0.5*(e2'*e2)/glm.c2; +% const=-0.5*N*log(2*pi); +% glm.logev=accuracy-complexity+const; +% glm.like=accuracy+const; +% glm.acc=accuracy; +% glm.kl=complexity; \ No newline at end of file diff --git a/toolbox/mci/models/linear/mci_linear_struct.m b/toolbox/mci/models/linear/mci_linear_struct.m new file mode 100644 index 00000000..6810c301 --- /dev/null +++ b/toolbox/mci/models/linear/mci_linear_struct.m @@ -0,0 +1,92 @@ +function [M,U,Xfull] = mci_linear_struct (Nobs,lambda,des) +% Set up data structures for linear model +% FORMAT [M,U,Xfull] = mci_linear_struct (Nobs,lambda,des) +% +% Nobs number of data points +% lambda noise precision +% des type of design +% +% M model structure +% U U.X is the design matrix +% Xfull Design matrix for data points [1:T] +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linear_struct.m 6548 2015-09-11 12:39:47Z will $ + +try design=des; catch design='linear-offset'; end + +% Number of time points +T=100; + +switch design + case 'dct', + order=7; + for j=1:order, + U.names{j}=sprintf('R%d',j); + end + U.X=spm_dctmtx(T,order); + case 'linear-offset', + x1=ones(T,1); + x2=[1:T]'; + x2=x2-mean(x2); + U.X=[x1,x2]; + U.names={'Offset','Linear'}; + case 'quadratic-offset', + x0=ones(T,1); + x1=[1:T]'; + x1=x1-mean(x1); + x2=x1.^2; + U.X=[x0,x1,x2]; + U.names={'Offset','Linear','Quadratic'}; + case 'quadratic', + x1=[1:T]'; + x1=x1-mean(x1); + x2=x1.^2; + U.X=[x1,x2]; + U.names={'Offset','Quadratic'}; + case 'unequal_var', + x1=[1:T]'; + x1=x1-mean(x1); + x2=x1.^2; + x2=x2/10; + U.X=[x2,x1]; + U.names={'Offset','Quadratic'}; + otherwise + disp(sprintf('Unknown design: %s, in linear_struct.m',design)); + return +end + +Xfull=U.X; + +t=[1:T]'; +% Thin observations to selected time points +if Nobs < T + rind=randperm(T); + ind=rind(1:Nobs); + ind=sort(ind); + U.X=U.X(ind,:); + t=t(ind); + U.ind=ind; +end + +sigma_e=sqrt(1/lambda); +M.Ce=1/lambda; +M.L='mci_linear_like'; +M.dL='mci_linear_deriv'; +M.IS='mci_linear_gen'; + +Np=size(U.X,2); +M.pE=zeros(Np,1); +%M.pC=0.5*eye(Np); +M.pC=10*eye(Np); +M.l=1; +M.t=t; +M.T=T; +M.N=T; + +M.Npflow=Np; +M.Npout=0; + + diff --git a/toolbox/mci/models/linsqr/mci_linsqr_deriv.m b/toolbox/mci/models/linsqr/mci_linsqr_deriv.m new file mode 100644 index 00000000..71ba0f05 --- /dev/null +++ b/toolbox/mci/models/linsqr/mci_linsqr_deriv.m @@ -0,0 +1,39 @@ +function [dLdp,iCpY,L] = mci_linsqr_deriv (P,M,U,Y) +% Gradient of likelihood for linear regression +% FORMAT [dLdp,iCpY,L] = mci_linsqr_deriv (P,M,U,Y) +% +% P parameters +% M model +% U inputs +% Y data +% +% dLdp gradient of log joint +% iCpY curvature (Fisher Information) +% L log joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linsqr_deriv.m 6548 2015-09-11 12:39:47Z will $ + +G = mci_linsqr_gen (P,M,U); +if isstruct(Y) + e = Y.y-G; +else + e = Y-G; +end +X = U.X; + +N=size(X,1); +dydp=2*(ones(N,1)*P').*X; + +%dydp = spm_diff(M.IS,P,M,U,1); + +dLdp=dydp'*M.iCe*e; +dLdp=dLdp'; + +iCpY=dydp'*M.iCe*dydp; + +if nargout > 2 + L=mci_linsqr_like (P,M,U,Y); +end \ No newline at end of file diff --git a/toolbox/mci/models/linsqr/mci_linsqr_gen.m b/toolbox/mci/models/linsqr/mci_linsqr_gen.m new file mode 100644 index 00000000..6c99970d --- /dev/null +++ b/toolbox/mci/models/linsqr/mci_linsqr_gen.m @@ -0,0 +1,18 @@ +function [y] = mci_linsqr_gen (theta,M,U) +% Output of linear model with squared params +% FORMAT [y] = mci_linsqr_gen (theta,M,U) +% +% theta regression coefficients +% M model structure +% U U.X contains design matrix +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linsqr_gen.m 6548 2015-09-11 12:39:47Z will $ + +% Square parameters +beta=theta(:).^2; + +y=U.X*beta; + diff --git a/toolbox/mci/models/linsqr/mci_linsqr_like.m b/toolbox/mci/models/linsqr/mci_linsqr_like.m new file mode 100644 index 00000000..683bf782 --- /dev/null +++ b/toolbox/mci/models/linsqr/mci_linsqr_like.m @@ -0,0 +1,31 @@ +function [L,E,st] = mci_linsqr_like (theta,M,U,Y) +% Compute log likelihood of linear model +% FORMAT [L,E,st] = mci_linsqr_like (theta,M,U,Y) +% +% theta regression coefficients +% M model +% U inputs +% Y data +% +% L Log likelihood +% E Errors +% st Status flag (0 for OK, -1 for problem) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linsqr_like.m 6548 2015-09-11 12:39:47Z will $ + +st=0; + +yhat = mci_linsqr_gen (theta,M,U); + +T=length(yhat); +if isstruct(Y) + E=sum(sum((Y.y-yhat).^2)); +else + E=sum(sum((Y-yhat).^2)); +end + +L = M.logdet_Ce - 0.5*T*log(2*pi); +L = L - 0.5*M.iCe*E; diff --git a/toolbox/mci/models/linsqr/mci_linsqr_struct.m b/toolbox/mci/models/linsqr/mci_linsqr_struct.m new file mode 100644 index 00000000..0f198a4f --- /dev/null +++ b/toolbox/mci/models/linsqr/mci_linsqr_struct.m @@ -0,0 +1,55 @@ +function [M,U,Xfull] = mci_linsqr_struct (Nobs,lambda,des) +% Set up data structures for linsqr model +% FORMAT [M,U,Xfull] = mci_linsqr_struct (Nobs,lambda,des) +% +% Nobs number of data points +% lambda noise precision +% des type of design +% +% M model structure +% U U.X is the design matrix +% Xfull Design matrix for data points [1:T] +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_linsqr_struct.m 6548 2015-09-11 12:39:47Z will $ + +% Number of time points +T=100; + +order=2; +for j=1:order, + U.names{j}=sprintf('R%d',j); +end +U.X=spm_dctmtx(T,order); + +Xfull=U.X; + +t=[1:T]'; +% Thin observations to selected time points +if Nobs < T + rind=randperm(T); + ind=rind(1:Nobs); + ind=sort(ind); + U.X=U.X(ind,:); + t=t(ind); + U.ind=ind; +end + +sigma_e=sqrt(1/lambda); +M.Ce=1/lambda; +M.L='mci_linsqr_like'; +M.dL='mci_linsqr_deriv'; +M.IS='mci_linsqr_gen'; + +Np=size(U.X,2); +M.pE=zeros(Np,1); +%M.pC=0.5*eye(Np); +M.pC=100*eye(Np); +M.l=1; +M.t=t; +M.T=T; +M.N=T; + + diff --git a/toolbox/mci/models/logistic/mci_logistic_act.m b/toolbox/mci/models/logistic/mci_logistic_act.m new file mode 100644 index 00000000..4dac48f2 --- /dev/null +++ b/toolbox/mci/models/logistic/mci_logistic_act.m @@ -0,0 +1,16 @@ +function [a] = mci_logistic_act (P,M,U) +% Activations of logistic model +% FORMAT [a] = mci_logistic_act (P,M,U) +% +% P parameters +% M model structure +% U contains rewards and times +% +% a activations of logistic model +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_logistic_act.m 6548 2015-09-11 12:39:47Z will $ + +a = U.X*P; \ No newline at end of file diff --git a/toolbox/mci/models/logistic/mci_logistic_deriv.m b/toolbox/mci/models/logistic/mci_logistic_deriv.m new file mode 100644 index 00000000..c9a1edd9 --- /dev/null +++ b/toolbox/mci/models/logistic/mci_logistic_deriv.m @@ -0,0 +1,28 @@ +function [dLdp,iCpY,L] = mci_logistic_deriv (P,M,U,Y) +% Gradient of likelihood for logistic model +% FORMAT [dLdp,iCpY,L] = mci_logistic_deriv (P,M,U,Y) +% +% P parameters +% M model +% U inputs +% Y data +% +% dLdp gradient of log joint +% iCpY curvature (Fisher Information) +% L log joint +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_logistic_deriv.m 6548 2015-09-11 12:39:47Z will $ + +dLdp = spm_diff(M.L,P,M,U,Y,1); +dLdp = full(dLdp(:)); + +g = mci_logistic_gen (P,M,U); +Lambda=diag(g.*(1-g)); +iCpY=U.X'*Lambda*U.X; + +if nargout > 2 + L=mci_logistic_like (P,M,U,Y); +end \ No newline at end of file diff --git a/toolbox/mci/models/logistic/mci_logistic_gen.m b/toolbox/mci/models/logistic/mci_logistic_gen.m new file mode 100644 index 00000000..f2a7f6b8 --- /dev/null +++ b/toolbox/mci/models/logistic/mci_logistic_gen.m @@ -0,0 +1,20 @@ +function [g,y] = mci_logistic_gen (P,M,U) +% Output of logistic regression model +% FORMAT [g,y] = mci_logistic_gen (P,M,U) +% +% P parameters +% M model structure +% U U.X contains design matrix +% +% g probabilities of y=1 +% y binary decisions based on g +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_logistic_gen.m 6548 2015-09-11 12:39:47Z will $ + +a = mci_logistic_act (P,M,U); +g = 1./(1+exp(-a)); + +y = g > rand(M.N,1); \ No newline at end of file diff --git a/toolbox/mci/models/logistic/mci_logistic_like.m b/toolbox/mci/models/logistic/mci_logistic_like.m new file mode 100644 index 00000000..5e0c7b87 --- /dev/null +++ b/toolbox/mci/models/logistic/mci_logistic_like.m @@ -0,0 +1,37 @@ +function [L,E,st] = mci_logistic_like (P,M,U,Y) +% Compute log likelihood of logistic model +% FORMAT [L,E,st] = mci_logistic_like (P,M,U,Y) +% +% P parameters +% M model +% U inputs +% Y data +% +% L Log likelihood +% E Errors +% st Status flag (0 for OK, -1 for problem) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_logistic_like.m 6548 2015-09-11 12:39:47Z will $ + +st=0; + +g = mci_logistic_gen (P,M,U); + +T=length(g); +if isstruct(Y) + y=Y.y; +else + y=Y; +end + +L=0; +for n=1:T, + L = L + y(n)*log(g(n)+eps)+(1-y(n))*log(1-g(n)+eps); +end +E=-L; + + + diff --git a/toolbox/mci/models/logistic/mci_logistic_struct.m b/toolbox/mci/models/logistic/mci_logistic_struct.m new file mode 100644 index 00000000..12f57e42 --- /dev/null +++ b/toolbox/mci/models/logistic/mci_logistic_struct.m @@ -0,0 +1,76 @@ +function [M,U,Y] = mci_logistic_struct (log_data,T) +% Set up data structures for logistic model +% FORMAT [M,U,Y] = mci_logistic_struct (log_data,T) +% +% log_data 'pima','ripley' or 'dct' +% T for 'dct' we can specify number of samples +% +% M model structure +% U U.X is the design matrix (independent variables) +% Y dependent variable +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_logistic_struct.m 6548 2015-09-11 12:39:47Z will $ + +try T=T; catch T=100; end + +switch log_data, + % Data is from Brian Ripley's archive + % http://www.stats.ox.ac.uk/pub/PRNN/ + % See also mci/data/README + + case 'pima', + var_name={'npreg','glu','bp','skin','bmi','ped','age'}; + load pima_tr.txt + load pima_te.txt + U.X=[pima_tr(:,1:7);pima_te(:,1:7)]; + Y=[pima_tr(:,8);pima_te(:,8)]; + T=size(U.X,1); + U.X=[U.X,ones(T,1)]; + prior_var=1; + + case 'ripley', + var_name={'x1','x2'}; + load synth_tr.txt + U.X=synth_tr(:,1:2); + Y=synth_tr(:,3); + T=size(U.X,1); + U.X=[U.X,ones(T,1)]; + prior_var=1; + + case 'dct', + var_name={'x1','x2','x3'}; + U.X=spm_dctmtx(T,3); + U.names=var_name; + prior_var=100; + Y=[]; + + case 'cos', + var_name={'x1','x2','x3'}; + U.names=var_name; + Tmax=1000; + t=[1:Tmax]/Tmax; + U.X(:,1)=ones(Tmax,1); + U.X(:,2)=cos(2*pi*t); + U.X(:,3)=cos(4*pi*t); + ind=randperm(Tmax); + s=sort(ind(1:T)); + U.X=U.X(s,:); + U.t=t(s); + prior_var=100; + Y=[]; +end + + +M.L='mci_logistic_like'; +M.dL='mci_logistic_deriv'; +M.IS='mci_logistic_gen'; +M.l=1; +M.T=T; +M.N=T; +Np=size(U.X,2); +M.pE=zeros(Np,1); +M.pC=prior_var*eye(Np); + diff --git a/toolbox/mci/models/nmm/mci_nmm_fx_delay.m b/toolbox/mci/models/nmm/mci_nmm_fx_delay.m new file mode 100644 index 00000000..396c9c41 --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_fx_delay.m @@ -0,0 +1,165 @@ +function [f] = mci_nmm_fx_delay (x,u,P,M) +% State equations for a neural mass model of erps with first order delays +% FORMAT [f] = mci_nmm_fx_delay (x,u,P,M) +% +% x - state vector +% x(:,1) - voltage (spiny stellate cells) +% x(:,2) - voltage (pyramidal cells) +ve +% x(:,3) - voltage (pyramidal cells) -ve +% x(:,4) - current (spiny stellate cells) depolarizing +% x(:,5) - current (pyramidal cells) depolarizing +% x(:,6) - current (pyramidal cells) hyperpolarizing +% x(:,7) - voltage (inhibitory interneurons) +% x(:,8) - current (inhibitory interneurons) depolarizing +% x(:,9) - voltage (pyramidal cells) +% +% f - dx(t)/dt = f(x(t)) +% +% Prior fixed parameter scaling [Defaults] +% +% M.pF.E = [32 16 4]; % extrinsic rates (forward, backward, lateral) +% M.pF.H = [1 4/5 1/4 1/4]*128; % intrinsic rates (g1, g2 g3, g4) +% M.pF.D = [2 16]; % propogation delays (intrinsic, extrinsic) +% M.pF.G = [4 32]; % receptor densities (excitatory, inhibitory) +% M.pF.T = [8 16]; % synaptic constants (excitatory, inhibitory) +% M.pF.R = [1 1/2]; % parameter of static nonlinearity +% +%__________________________________________________________________________ +% David O, Friston KJ (2003) A neural mass model for MEG/EEG: coupling and +% neuronal dynamics. NeuroImage 20: 1743-1755 +% +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_nmm_fx_delay.m 6548 2015-09-11 12:39:47Z will $ + +if ~isstruct(P) +% if isfield(M,'V') +% % Project params out of subspace +% P=M.V*P(:); +% end + P=spm_unvec(P,M.pE); +end + +% get dimensions and configure state variables +%-------------------------------------------------------------------------- +n = length(P.A{1}); % number of sources +x = spm_unvec(x,M.x); % neuronal states + +% [default] fixed parameters +%-------------------------------------------------------------------------- +E = [1 1/2 1/8]*32; % extrinsic rates (forward, backward, lateral) +G = [1 4/5 1/4 1/4]*128; % intrinsic rates (g1 g2 g3 g4) +D = [2 16]; % propagation delays (intrinsic, extrinsic) +H = [4 32]; % receptor densities (excitatory, inhibitory) +T = [8 16]; % synaptic constants (excitatory, inhibitory) +R = [2 1]/3; % parameters of static nonlinearity + +% [specified] fixed parameters +%-------------------------------------------------------------------------- +if isfield(M,'pF') + try, E = M.pF.E; end + try, G = M.pF.H; end + try, D = M.pF.D; end + try, H = M.pF.G; end + try, T = M.pF.T; end + try, R = M.pF.R; end +end + +% test for free parameters on intrinsic connections +%-------------------------------------------------------------------------- +try + G = G.*exp(P.H); +end +G = ones(n,1)*G; + +% exponential transform to ensure positivity constraints +%-------------------------------------------------------------------------- +A{1} = exp(P.A{1})*E(1); +A{2} = exp(P.A{2})*E(2); +A{3} = exp(P.A{3})*E(3); +C = exp(P.C); + +% intrinsic connectivity and parameters +%-------------------------------------------------------------------------- +Te = T(1)/1000*exp(P.T(:,1)); % excitatory time constants +Ti = T(2)/1000*exp(P.T(:,2)); % inhibitory time constants +He = H(1)*exp(P.G(:,1)); % excitatory receptor density +Hi = H(2)*exp(P.G(:,2)); % inhibitory receptor density + +% delays +%-------------------------------------------------------------------------- +De = D(2).*exp(P.D)/1000; % extrinsic +Di = D(1)/1000; % intrinsic + +% presynaptic i/p from delayed voltages using first order Taylor approx +% v(t-tau) = v(t) - tau dv/dt +%-------------------------------------------------------------------------- +pyr_int = presynaptic (x(:,9)-Di*(x(:,5)-x(:,6)),P,R); % Intrinsic delays +stl_int = presynaptic (x(:,1)-Di*x(:,4),P,R); +inh_int = presynaptic (x(:,7)-Di*x(:,8),P,R); +pyr_ext = presynaptic (x(:,9)-De*(x(:,5)-x(:,6)),P,R); % Extrinsic delays + +% input +%========================================================================== +if isfield(M,'u') + + % endogenous input + %---------------------------------------------------------------------- + U = u(:)*64; + +else + % exogenous input + %---------------------------------------------------------------------- + U = C*u(:)*2; +end + +% State: f(x) +%========================================================================== + +%f=zeros(size(M.x)); + +% Supragranular layer (inhibitory interneurons): Voltage & depolarizing current +%-------------------------------------------------------------------------- +f(:,7) = x(:,8); +f(:,8) = (He.*((A{2} + A{3})*pyr_ext + G(:,3).*pyr_int) - 2*x(:,8) - x(:,7)./Te)./Te; + +% Granular layer (spiny stellate cells): Voltage & depolarizing current +%-------------------------------------------------------------------------- +f(:,1) = x(:,4); +f(:,4) = (He.*((A{1} + A{3})*pyr_ext + G(:,1).*pyr_int + U) - 2*x(:,4) - x(:,1)./Te)./Te; + +% Infra-granular layer (pyramidal cells): depolarizing current +%-------------------------------------------------------------------------- +f(:,2) = x(:,5); +f(:,5) = (He.*((A{2} + A{3})*pyr_ext + G(:,2).*stl_int) - 2*x(:,5) - x(:,2)./Te)./Te; + +% Infra-granular layer (pyramidal cells): hyperpolarizing current +%-------------------------------------------------------------------------- +f(:,3) = x(:,6); +f(:,6) = (Hi.*G(:,4).*inh_int - 2*x(:,6) - x(:,3)./Ti)./Ti; + +% Infra-granular layer (pyramidal cells): Voltage +%-------------------------------------------------------------------------- +f(:,9) = x(:,5) - x(:,6); + +%f = spm_vec(f); +f=f(:); + +end + +function [S] = presynaptic (x,P,R) + +% pre-synaptic inputs: s(V) +%-------------------------------------------------------------------------- +R = R.*exp(P.S); +S = 1./(1 + exp(-R(1)*(x - R(2)))) - 1./(1 + exp(R(1)*R(2))); + +end + + + + + + diff --git a/toolbox/mci/models/nmm/mci_nmm_gen.m b/toolbox/mci/models/nmm/mci_nmm_gen.m new file mode 100644 index 00000000..5ef6bfd2 --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_gen.m @@ -0,0 +1,18 @@ +function [Y] = mci_nmm_gen (M,U,P) +% Generate data from two region NMM +% FORMAT [Y] = mci_nmm_gen (M,U,P) +% +% M Model structure +% U Inputs +% P Parameters +% +% Y Data +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_nmm_gen.m 6548 2015-09-11 12:39:47Z will $ + +[G,x] = spm_mci_fwd (P,M,U); +e=randn(M.N,M.Nr)*sqrt(M.Ce); +Y=G+e; diff --git a/toolbox/mci/models/nmm/mci_nmm_params.m b/toolbox/mci/models/nmm/mci_nmm_params.m new file mode 100644 index 00000000..5364f93e --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_params.m @@ -0,0 +1,15 @@ +function [P] = mci_nmm_params (M,U) +% Generate parameters for two region NMM +% FORMAT [P] = mci_nmm_params (M,U) +% +% M Model structure +% U Inputs +% +% P Parameters +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_nmm_params.m 6548 2015-09-11 12:39:47Z will $ + +P=spm_normrnd(M.pE,M.pC,1); diff --git a/toolbox/mci/models/nmm/mci_nmm_r2_gx.m b/toolbox/mci/models/nmm/mci_nmm_r2_gx.m new file mode 100644 index 00000000..78aef07b --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_r2_gx.m @@ -0,0 +1,25 @@ +function [y,L] = mci_nmm_r2_gx (x,u,P,M) +% Observation function for 2-region NMM +% FORMAT [y,L] = mci_nmm_r2_gx (x,u,P,M) +% +% P Parameters +% M Model structure +% U Inputs +% +% y Output +% L Lead field (dy/dx) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_nmm_r2_gx.m 6548 2015-09-11 12:39:47Z will $ + +x=spm_vec(x); + +L=zeros(2,18); +L(1,17)=1; +L(2,18)=1; +y=L*x; + + + diff --git a/toolbox/mci/models/nmm/mci_nmm_r2p2_dfdp.m b/toolbox/mci/models/nmm/mci_nmm_r2p2_dfdp.m new file mode 100644 index 00000000..46c1dcd3 --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_r2p2_dfdp.m @@ -0,0 +1,66 @@ +function [F] = mci_nmm_r2p2_dfdp (x,u,P,M) +% Parameter Jacobian for two region, two parameter NMM +% FORMAT [F] = mci_nmm_r2p2_dfdp (x,u,P,M) +% +% x State +% u Inputs +% P Parameters +% M Model structure +% +% F F(i,j) = df(x)_i/dp_j +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_nmm_r2p2_dfdp.m 6548 2015-09-11 12:39:47Z will $ + +% 18 state variables, 2 parameters +F=zeros(18,2); + +curr_P=M.can_P; % Canonical parameter set + +w21=P(1); +w12=P(2); + +% 2 free parameters +curr_P.A{1}(2,1)=P(1); % Forward connection, w_21 +curr_P.A{2}(1,2)=P(2); % Backward connection, w_12 + +P=curr_P; + +% default parameters +E = [1 1/2 1/8]*32; % extrinsic rates (forward, backward, lateral) +D = [2 16]; % propogation delays (intrinsic, extrinsic) +H = [4 32]; % receptor densities (excitatory, inhibitory) +T = [8 16]; % synaptic constants (excitatory, inhibitory) +R = [2 1]/3; % parameters of static nonlinearity + +% neuronal states into matrix form; x(r,:) for region r +x = spm_unvec(x,M.x); +% extrinsic delays +De = D(2).*exp(P.D)/1000; + +% delayed pyramidal cell activity +pyr_ext = presynaptic (x(:,9)-De*(x(:,5)-x(:,6)),P,R); + +Te = T(1)/1000*exp(P.T(:,1)); % excitatory time constants +He = H(1)*exp(P.G(:,1)); % excitatory receptor density + +HeTe=He/Te; + +% Effect of forward connection on region 2 stellate cells +F(8,1)=HeTe*pyr_ext(1)*E(1)*w21*exp(w12); +% Effect of backward connection on region 1 pyramidal cells +F(9,2)=HeTe*pyr_ext(2)*E(2)*w12*exp(w12); +% Effect of backward connection on region 1 inhibitory cells +F(15,2)=HeTe*pyr_ext(2)*E(2)*w12*exp(w12); +end + +function [S] = presynaptic (x,P,R) + +% pre-synaptic inputs: s(V) +%-------------------------------------------------------------------------- +R = R.*exp(P.S); +S = 1./(1 + exp(-R(1)*(x - R(2)))) - 1./(1 + exp(R(1)*R(2))); + +end diff --git a/toolbox/mci/models/nmm/mci_nmm_r2p2_dfdx.m b/toolbox/mci/models/nmm/mci_nmm_r2p2_dfdx.m new file mode 100644 index 00000000..18de99ee --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_r2p2_dfdx.m @@ -0,0 +1,29 @@ +function [F] = mci_nmm_r2p2_dfdp (x,u,P,M) +% State Jacobian for two region, two parameter NMM +% FORMAT [F] = mci_nmm_r2p2_dfdp (x,u,P,M) +% +% x State +% u Inputs +% P Parameters +% M Model structure +% +% F F(i,j) = df(x)_i/dtheta_j +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_nmm_r2p2_dfdx.m 6548 2015-09-11 12:39:47Z will $ + +% 18 state variables +F = zeros(18,18); + +% f Flow, dx/dt + +curr_P=M.can_P; % Canonical parameter set + +% 2 free parameters +curr_P.A{1}(2,1)=P(1); % Forward connection, w_21 +curr_P.A{2}(1,2)=P(2); % Backward connection, w_12 + +f = mci_nmm_fx_delay(x,u,curr_P,M); + diff --git a/toolbox/mci/models/nmm/mci_nmm_r2p2_fx.m b/toolbox/mci/models/nmm/mci_nmm_r2p2_fx.m new file mode 100644 index 00000000..dc628a00 --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_r2p2_fx.m @@ -0,0 +1,24 @@ +function [f] = mci_nmm_r2p2_fx (x,u,P,M) +% Flow for two region, two parameter NMM +% FORMAT [f] = mci_nmm_r2p2_fx (x,u,P,M) +% +% x State +% u Inputs +% P Parameters +% M Model structure +% +% f Flow, dx/dt +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_nmm_r2p2_fx.m 6548 2015-09-11 12:39:47Z will $ + +curr_P=M.can_P; % Canonical parameter set + +% 2 free parameters +curr_P.A{1}(2,1)=P(1); % Forward connection, w_21 +curr_P.A{2}(1,2)=P(2); % Backward connection, w_12 + +f = mci_nmm_fx_delay(x,u,curr_P,M); + diff --git a/toolbox/mci/models/nmm/mci_nmm_r2p6_fx.m b/toolbox/mci/models/nmm/mci_nmm_r2p6_fx.m new file mode 100644 index 00000000..7cd80f0f --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_r2p6_fx.m @@ -0,0 +1,29 @@ +function [f] = mci_nmm_r2p6_fx (x,u,P,M) +% Flow for two region, six parameter NMM +% FORMAT [f] = mci_nmm_r2p6_fx (x,u,P,M) +% +% x State +% u Inputs +% P Parameters +% M Model structure +% +% f Flow, dx/dt +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_nmm_r2p6_fx.m 6548 2015-09-11 12:39:47Z will $ + +curr_P=M.can_P; % Canonical parameter set + +% Extrinsic connections +curr_P.A{1}(2,1)=P(1); % Forward connection, w_21 +curr_P.A{2}(1,2)=P(2); % Backward connection, w_12 + +% Intrinsic connections +curr_P.H(1)=P(3); +curr_P.H(2)=P(4); +curr_P.H(3)=P(5); +curr_P.H(4)=P(6); + +f = mci_nmm_fx_delay(x,u,curr_P,M); diff --git a/toolbox/mci/models/nmm/mci_nmm_struct.m b/toolbox/mci/models/nmm/mci_nmm_struct.m new file mode 100644 index 00000000..c833c975 --- /dev/null +++ b/toolbox/mci/models/nmm/mci_nmm_struct.m @@ -0,0 +1,108 @@ +function [M,U] = mci_nmm_struct (back,sd,Np) +% Set up two region NMM +% FORMAT [M,U] = mci_nmm_struct (back,sd,Np) +% +% back 1 to include backward connection (default) +% sd Observation noise SD (default 0.01) +% Np number of params (2,6 or 21) +% +% M Model structure +% U Inputs +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_nmm_struct.m 6548 2015-09-11 12:39:47Z will $ + +if nargin < 1 | isempty(back) + back=1; +end +try lambda=1/(sd^2); catch lambda=10000; end + +M.Nr=2; + +%M.N=1000; +M.N=250; +M.T=0.4; +M.x=zeros(M.Nr,9); +M.x0=zeros(M.Nr*9,1); + +M.n=length(M.x0); % n states +M.l=2; % l outputs + +% Specify input +M.ons=64; +M.dur=16; +PP.R=[0 0]; +dt=M.T/M.N; +M.t=[1:M.N]'*dt; + +M.g='mci_nmm_r2_gx'; + +% Specify connectivity pattern +A{1}=[0 0; 1 0]; % Extrinsic forward +if back + A{2}=[0 1; 0 0]; % Extrinsic backward +else + A{2}=[0 0; 0 0]; % Extrinsic backward +end +A{3}=[0 0; 0 0]; % Extrinsic lateral +B=[]; + +% Priors +% spm_erp_priors.m has prior SD of 0.25 on connex +prior_sd=0.4; +switch Np + case 2, + M.pE=zeros(2,1); + M.pC=prior_sd^2*eye(2); + M.m=2; % m inputs + u(1,:)=spm_erp_u(M.t,PP,M); + u(2,:)=zeros(1,M.N); + U=full(u); + M.f='mci_nmm_r2p2_fx'; + C=1; + [M.can_P,pC] = spm_erp_priors(A,B,C); + M.can_P.C=0; + + case 6, + M.pE=zeros(6,1); + M.pC=prior_sd^2*eye(6); + M.m=2; % m inputs + u(1,:)=spm_erp_u(M.t,PP,M); + u(2,:)=zeros(1,M.N); + U=full(u); + M.f='mci_nmm_r2p6_fx'; + C=1; + [M.can_P,pC] = spm_erp_priors(A,B,C); + M.can_P.C=0; + case 21, + M.pC.A{1}(2,1)=prior_sd^2; % Forward connection + M.pC.A{2}(1,2)=prior_sd^2; % Backward connection + M.m=1; % m inputs + u(1,:)=spm_erp_u(M.t,PP,M); + U=full(u); + M.f='mci_nmm_fx_delay'; + C=[1 0]'; + [M.pE,M.pC] = spm_erp_priors(A,B,C); + M.vpE=spm_vec(M.pE); + otherwise + disp('Unknown Np value in nmm_struct.m'); +end + +%M.int='euler'; +%M.int='ode15'; +M.int='sundials'; + +%M.ipC=inv(M.pC); +M.Np=length(M.pE); +M.vpE=spm_vec(M.pE); + +% Likelihood function +obs_sd=sqrt(1/lambda); +M.L='spm_mci_glike'; +M.Ce=obs_sd^2*eye(M.Nr); + +M.Npflow=length(M.pE); +M.Npout=0; + diff --git a/toolbox/mci/models/phase/mci_phase_dfdp.m b/toolbox/mci/models/phase/mci_phase_dfdp.m new file mode 100644 index 00000000..a02902b8 --- /dev/null +++ b/toolbox/mci/models/phase/mci_phase_dfdp.m @@ -0,0 +1,41 @@ +function [dfdp] = mci_phase_dfdp (x,u,P,M) +% Parameter sensitivity for phase model +% FORMAT [dfdp] = mci_phase_dfdp (x,u,P,M) +% +% x State vector +% u inputs +% P parameter vector +% M model structure +% +% dfdp Jacobian wrt. parameters, df/dp +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_phase_dfdp.m 6548 2015-09-11 12:39:47Z will $ + +D = M.n; +df_dp=zeros(D,length(P)); + +for k=1:D, + % kth state variable + for i=1:D, + for j=1:D, + % i,jth parameters in a,b matrices + if i==j + da(i,j)=0; + db(i,j)=0; + else + % derivative only non-zero for k=i + da(i,j)=(k==i)*sin(x(i)-x(j)); + db(i,j)=(k==i)*cos(x(i)-x(j)); + end + end + dw(i)=(k==i); + end + % Concatenate in order produced by spm_vec + dfdp(k,:)=[db(:)',da(:)',dw(:)']; +end + + + diff --git a/toolbox/mci/models/phase/mci_phase_dfdx.m b/toolbox/mci/models/phase/mci_phase_dfdx.m new file mode 100644 index 00000000..7c8746c4 --- /dev/null +++ b/toolbox/mci/models/phase/mci_phase_dfdx.m @@ -0,0 +1,42 @@ +function [dfdx] = mci_phase_dfdx (x,u,P,M) +% State sensitivity for phase model +% FORMAT [dfdx] = mci_phase_dfdx (x,u,P,M) +% +% x state vector +% M model structure +% P parameter vector +% +% dfdx Jacobian wrt states +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_phase_dfdx.m 6548 2015-09-11 12:39:47Z will $ + +params = spm_unvec (P,M.pE); + +% unpack parameters +a = params.sinCoeff; +b = params.cosCoeff; +D = M.n; +dfdx = zeros(D,D); + +ind=1:D; + +for i = 1:D, + ind_sum = [setdiff(ind,i) setdiff(i,ind)]; + for j = 1:D, + if i == j + % diagonal elements + tmp=0; + for k = 1:length(ind_sum) + jj=ind_sum(k); + tmp = tmp + a(i,jj).*cos(x(i) - x(jj)) - b(i,jj).*sin(x(i) - x(jj)) ; + end + dfdx(i,j) = tmp; + else + % off-diagonal elements + dfdx(i,j) = -a(i,j).*cos(x(i) - x(j)) + b(i,j).*sin(x(i) - x(j)); + end + end +end \ No newline at end of file diff --git a/toolbox/mci/models/phase/mci_phase_fx.m b/toolbox/mci/models/phase/mci_phase_fx.m new file mode 100644 index 00000000..fd564290 --- /dev/null +++ b/toolbox/mci/models/phase/mci_phase_fx.m @@ -0,0 +1,36 @@ +function [f] = mci_phase_fx (x,u,P,M) +% Flow function for phase model +% FORMAT [f] = mci_phase_fx (x,u,P,M) +% +% x state vector +% u inputs +% P parameter vector +% M model structure +% +% f dx/dt +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_phase_fx.m 6548 2015-09-11 12:39:47Z will $ + +params = spm_unvec (P,M.pE); + +a = params.sinCoeff; +b = params.cosCoeff; +w = params.intrinPhase; + +D = M.n; +% initialise state matrix +f = zeros(D,1); + +for i = 1:D, + f(i)=w(i); + for j = 1:D, + if ~(i==j) + f(i) = f(i) + a(i,j)*sin(x(i)-x(j)) + b(i,j)*cos(x(i)-x(j)); + end + end +end + + diff --git a/toolbox/mci/models/phase/mci_phase_gx.m b/toolbox/mci/models/phase/mci_phase_gx.m new file mode 100644 index 00000000..8986e079 --- /dev/null +++ b/toolbox/mci/models/phase/mci_phase_gx.m @@ -0,0 +1,11 @@ +function [y,L] = mci_phase_gx (x,u,P,M) +% Observation function for phase model +% FORMAT [y,L] = mci_phase_gx (x,u,P,M) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_phase_gx.m 6548 2015-09-11 12:39:47Z will $ + +L=eye(M.n); +y=L*x; \ No newline at end of file diff --git a/toolbox/mci/models/phase/mci_phase_init.m b/toolbox/mci/models/phase/mci_phase_init.m new file mode 100644 index 00000000..ebd5e1f8 --- /dev/null +++ b/toolbox/mci/models/phase/mci_phase_init.m @@ -0,0 +1,59 @@ +function [P,M,U,Y] = mci_phase_init (d) +% Initialise weakly coupled oscillator model +% FORMAT [P,M,U,Y] = mci_phase_init (d) +% +% d number of oscillators +% +% P parameters (drawn from prior) +% M model structure +% U inputs +% Y data +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_phase_init.m 6548 2015-09-11 12:39:47Z will $ + +M.N=200; +M.T=1; +dt=M.T/M.N; +M.t=[1:M.N]'*dt; +M.f='mci_phase_fx'; +M.g='mci_phase_gx'; +M.dfdp='mci_phase_dfdp'; +M.dfdx='mci_phase_dfdx'; + +% Initial states +M.x0=rand(d,1); +M.x=zeros(d,1); +M.m=0; +M.n=d; +M.l=d; + +nodes=M.n; + +params.cosCoeff = zeros(nodes); +params.sinCoeff = zeros(nodes); +params.intrinPhase = zeros(nodes,1); + +pE=[zeros(2*nodes^2,1); 3*ones(nodes,1)]; +Np=length(pE); +M.pE=spm_unvec(pE,params); +M.vpE=pE; +M.pC=0.1^2*eye(Np); +P=spm_normrnd(M.vpE,M.pC,1); + +M.Np=Np; +M.ipC=inv(M.pC); + +%M.int='euler'; +%M.int='ode15'; +M.int='sundials'; +U=zeros(1,M.N); + +% Likelihood function +obs_sd=0.1; +M.L='spm_mci_glike'; +M.Ce=obs_sd^2*eye(d); + +Y = spm_mci_fwd (P,M,U); \ No newline at end of file diff --git a/toolbox/mci/models/phase/mci_rphase_dfdp.m b/toolbox/mci/models/phase/mci_rphase_dfdp.m new file mode 100644 index 00000000..5e21e898 --- /dev/null +++ b/toolbox/mci/models/phase/mci_rphase_dfdp.m @@ -0,0 +1,19 @@ +function [dfdp] = mci_rphase_dfdp (x,u,P,M) +% Parameter sensitivity for phase model +% FORMAT [dfdp] = mci_rphase_dfdp (x,u,P,M) +% +% x State vector +% u inputs +% P parameter vector +% M model structure +% +% dfdp Jacobian wrt. parameters, df/dp +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_rphase_dfdp.m 6548 2015-09-11 12:39:47Z will $ + +dfdp = spm_diff(M.f,x,u,P,M,3); + + diff --git a/toolbox/mci/models/phase/mci_rphase_dfdx.m b/toolbox/mci/models/phase/mci_rphase_dfdx.m new file mode 100644 index 00000000..e9e9d189 --- /dev/null +++ b/toolbox/mci/models/phase/mci_rphase_dfdx.m @@ -0,0 +1,56 @@ +function [dfdx] = mci_rphase_dfdx (x,u,P,M) +% State sensitivity for phase model (reduced connectivity) +% FORMAT [dfdx] = mci_rphase_dfdx (x,u,P,M) +% +% x state vector +% M model structure +% P parameter vector +% +% dfdx Jacobian wrt states +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_rphase_dfdx.m 6548 2015-09-11 12:39:47Z will $ + +params = spm_unvec (P,M.pE); + +aconn = params.aconn; +bconn = params.bconn; + +Nc=length(M.conn); +a=zeros(M.n,M.n); +b=zeros(M.n,M.n); +for c=1:Nc, + i=M.conn(c).regions(1); + j=M.conn(c).regions(2); + a(i,j)=aconn(c); + b(i,j)=bconn(c); +end + +D = M.n; +dfdx = zeros(D,D); + +ind=1:D; + +for i = 1:D, + ind_sum = [setdiff(ind,i) setdiff(i,ind)]; + for j = 1:D, + if i == j + % diagonal elements + tmp=0; + for k = 1:length(ind_sum) + jj=ind_sum(k); + tmp = tmp + a(i,jj).*cos(x(i) - x(jj)) - b(i,jj).*sin(x(i) - x(jj)) ; + end + dfdx(i,j) = tmp; + else + % off-diagonal elements + dfdx(i,j) = -a(i,j).*cos(x(i) - x(j)) + b(i,j).*sin(x(i) - x(j)); + end + end +end + +dfdx=2*pi*dfdx; + +%dfdx_chk = spm_diff(M.f,x,u,P,M,1) diff --git a/toolbox/mci/models/phase/mci_rphase_fx.m b/toolbox/mci/models/phase/mci_rphase_fx.m new file mode 100644 index 00000000..126a685c --- /dev/null +++ b/toolbox/mci/models/phase/mci_rphase_fx.m @@ -0,0 +1,41 @@ +function [f] = mci_rphase_fx (x,u,P,M) +% Flow function for phase model +% FORMAT [f] = mci_rphase_fx (x,u,P,M) +% +% x state vector +% u inputs +% P parameter vector +% M model structure +% +% f dx/dt +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny and Biswa Sengupta +% $Id: mci_rphase_fx.m 6548 2015-09-11 12:39:47Z will $ + +params = spm_unvec (P,M.pE); + +aconn = params.aconn; +bconn = params.bconn; + +Nc=length(M.conn); +a=zeros(M.n,M.n); +b=zeros(M.n,M.n); +for c=1:Nc, + i=M.conn(c).regions(1); + j=M.conn(c).regions(2); + a(i,j)=aconn(c); + b(i,j)=bconn(c); +end + +for i = 1:M.n, + f(i)=M.freq; + for j = 1:M.n, + if ~(i==j) + f(i) = f(i) + a(i,j)*sin(x(i)-x(j)) + b(i,j)*cos(x(i)-x(j)); + end + end +end +f=2*pi*f(:); + diff --git a/toolbox/mci/models/phase/mci_rphase_gen.m b/toolbox/mci/models/phase/mci_rphase_gen.m new file mode 100644 index 00000000..b4a19966 --- /dev/null +++ b/toolbox/mci/models/phase/mci_rphase_gen.m @@ -0,0 +1,16 @@ +function [Y] = mci_rphase_gen (P,M,U) +% Generate data from reduced WCO model +% FORMAT [Y] = mci_rphase_gen (P,M,U) +% +% P parameters +% M model structure +% U inputs +% +% Y data +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_rphase_gen.m 6548 2015-09-11 12:39:47Z will $ + +Y = spm_mci_fwd (P,M,U); \ No newline at end of file diff --git a/toolbox/mci/models/phase/mci_rphase_struct.m b/toolbox/mci/models/phase/mci_rphase_struct.m new file mode 100644 index 00000000..29051326 --- /dev/null +++ b/toolbox/mci/models/phase/mci_rphase_struct.m @@ -0,0 +1,55 @@ +function [M,U] = mci_rphase_init (d,conn) +% Initialise weakly coupled oscillator model - reduced connectivity +% FORMAT [M,U] = mci_rphase_init (d,conn) +% +% d number of oscillators +% +% M model structure +% U inputs +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_rphase_struct.m 6548 2015-09-11 12:39:47Z will $ + +M.N=100; +M.T=1; +dt=0.01; +M.t=[1:M.N]'*dt; +M.f='mci_rphase_fx'; +M.g='mci_phase_gx'; +M.dfdp='mci_rphase_dfdp'; +M.dfdx='mci_rphase_dfdx'; + +% Initial states +M.x0=rand(d,1); +M.x=zeros(d,1); +M.m=0; +M.n=d; +M.l=d; + +nodes=M.n; + +Nc=length(conn); +Np=2*Nc; + +M.freq=6; +M.pE.aconn=zeros(Nc,1); +M.pE.bconn=zeros(Nc,1); +M.vpE=spm_vec(M.pE); +M.pC=eye(Np); + +M.Np=Np; +M.ipC=inv(M.pC); + +%M.int='euler'; +%M.int='ode15'; +M.int='sundials'; +U=zeros(1,M.N); + +% Likelihood function +obs_sd=0.1; +M.L='spm_mci_glike'; +M.Ce=obs_sd^2*eye(d); + +M.conn=conn; diff --git a/toolbox/mci/models/ramsay/mci_ramsay_fx.m b/toolbox/mci/models/ramsay/mci_ramsay_fx.m new file mode 100644 index 00000000..c59300f4 --- /dev/null +++ b/toolbox/mci/models/ramsay/mci_ramsay_fx.m @@ -0,0 +1,35 @@ +function [F] = mci_ramsay_fx (x,U,P,M) +% State equation for Ramsay model +% FORMAT [F] = mci_ramsay_fx (x,U,P,M) +% +% x State vector +% x(1) Voltage variable +% x(2) Recovery variable +% U inputs +% P vector of model parameters - 2 params only +% M model +% +% F dx/dt +% +% J Ramsay et al (2007) Parameter estimation for differential equations: +% a generalised smoothing approach. J Roy Stat Soc B, 69(5):741-796. +% +% See also section 10 (page 26) and contribution by W.Penny on page 75 of: +% +% Girolami and Calderhead (2011) Riemann manifold Langevin and Hamiltonian +% Monte Carlo methods. J Roy Stat Soc B,73(2):123-214. +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_ramsay_fx.m 6548 2015-09-11 12:39:47Z will $ + +c=3; + +P(1)=exp(P(1)); +P(2)=exp(P(2)); + +F=zeros(2,1); +F(1)=c*(x(1)-(1/3)*x(1)^3+x(2)); +F(2)=-(1/c)*(x(1)-P(1)+P(2)*x(2)); + diff --git a/toolbox/mci/models/ramsay/mci_ramsay_gen.m b/toolbox/mci/models/ramsay/mci_ramsay_gen.m new file mode 100644 index 00000000..0931ce05 --- /dev/null +++ b/toolbox/mci/models/ramsay/mci_ramsay_gen.m @@ -0,0 +1,19 @@ +function [Y] = mci_ramsay_gen (P,M,U) +% Generate data from Ramsay model +% FORMAT [Y] = mci_ramsay_gen (P,M,U) +% +% P Parameters +% M Model structure +% U Inputs +% +% Y Data +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_ramsay_gen.m 6548 2015-09-11 12:39:47Z will $ + +[G,x] = spm_mci_fwd (P,M,U); +[N,l] = size(G); +e = randn(N,l)*sqrt(M.Ce); +Y = G + e; diff --git a/toolbox/mci/models/ramsay/mci_ramsay_gx.m b/toolbox/mci/models/ramsay/mci_ramsay_gx.m new file mode 100644 index 00000000..a70c9abd --- /dev/null +++ b/toolbox/mci/models/ramsay/mci_ramsay_gx.m @@ -0,0 +1,18 @@ +function [y,L] = spm_ramsay_gx (x,u,P,M) +% Observation equation for Ramsay model +% FORMAT [y,L] = spm_ramsay_gx (x,u,P,M) +% +% x,u,P,M state,input,params,model +% +% y observations +% L dy/dx +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_ramsay_gx.m 6548 2015-09-11 12:39:47Z will $ + +nx=length(x); +L=eye(nx); +y=x; + diff --git a/toolbox/mci/models/ramsay/mci_ramsay_struct.m b/toolbox/mci/models/ramsay/mci_ramsay_struct.m new file mode 100644 index 00000000..824c076f --- /dev/null +++ b/toolbox/mci/models/ramsay/mci_ramsay_struct.m @@ -0,0 +1,48 @@ +function [M,U] = mci_ramsay_struct (sigma_e) +% Data structures for Ramsay model +% FORMAT [M,U] = mci_ramsay_struct (sigme_e) +% +% sigma_e Noise SD +% +% M,U model, input data structures +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_ramsay_struct.m 6548 2015-09-11 12:39:47Z will $ + +ms=20; +dt=0.1; + +T=ms/dt; +U.dt=dt; +U.u=zeros(T,1); +U.tims=[1:T]'*dt; + +M.t=U.tims; +M.T=T; +M.N=length(U.tims); + +M.x0 = [-1 1]'; +M.x = M.x0; +%M.ns = T; + +M.m=1; % number of inputs +M.n=length(M.x0); % n states +M.l=2; % l outputs + +M.f = 'mci_ramsay_fx'; +M.g = 'mci_ramsay_gx'; +M.L = 'spm_mci_glike'; + +%M.int='euler'; +%M.int='ode15'; +M.int='sundials'; + +M.pE = [log(0.5),log(0.5)]'; +M.vpE = spm_vec(M.pE); +M.pC = [1/8 0; 0 1/8]; +M.ipC = inv(M.pC); +M.Np = length(M.pE); + +M.Ce = sigma_e^2*eye(2); diff --git a/toolbox/mci/plotting/mci_plot_dist.m b/toolbox/mci/plotting/mci_plot_dist.m new file mode 100644 index 00000000..f679a17f --- /dev/null +++ b/toolbox/mci/plotting/mci_plot_dist.m @@ -0,0 +1,51 @@ +function [] = mci_plot_dist (dist,j,xlims) +% Plot probability density +% FORMAT [] = mci_plot_dist (dist,j,xlims) +% +% dist struct with fields +% +% .Ep posterior mean +% .P [Np x Ns] sample matrix +% .ind indices of samples dist burn-in +% .names +% .ks set to 1 for kernel smoothing (default) +% j jth variable +% xlims xlims(1,2) for lower/upper limits +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_plot_dist.m 6548 2015-09-11 12:39:47Z will $ + +try ks=dist.ks; catch ks=1; end + +if nargin > 2 + limits=1; +else + limits=0; +end +lw=2; + +switch lower(dist.type), + case 'boxplot', + boxplot(dist.P(j,dist.ind)); + case 'sample', + if ks + [g,xi]=ksdensity(dist.P(j,dist.ind)); + plot(xi,g,dist.color,'LineWidth',lw);set(gca,'YTick',[]); + else + hist(dist.P(j,dist.ind),20); + end + case 'gaussian', + m=dist.Ep(j);s=sqrt(full(dist.Cp(j,j))); + xi=linspace(m-4*s,m+4*s,100); + g=spm_Npdf(xi,m,s^2); + plot(xi,g,dist.color,'LineWidth',lw);set(gca,'YTick',[]); +end +set(gca,'FontSize',16); +xlabel(dist.names{j}); +grid on + +if limits + xlim([xlims(1) xlims(2)]); +end \ No newline at end of file diff --git a/toolbox/mci/plotting/mci_plot_dist_multi.m b/toolbox/mci/plotting/mci_plot_dist_multi.m new file mode 100644 index 00000000..83609829 --- /dev/null +++ b/toolbox/mci/plotting/mci_plot_dist_multi.m @@ -0,0 +1,95 @@ +function [] = mci_plot_dist_multi (dist,name,P) +% Plot (multiple) densities +% FORMAT [] = mci_plot_dist_multi (dist,name,P) +% +% dist{i} ith distribution +% +% .Ep mean +% .P [Np x Ns] sample matrix +% .ind indices of samples (eg. post burn-in) +% .names names of variables +% .color eg 'r','k','b' +% .order eg. [1,3,4,2] to plot only variables 1,3,4 and 2 in that order +% +% name name of parameters +% P true parameters (optional) +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_plot_dist_multi.m 6548 2015-09-11 12:39:47Z will $ + +Ndist=length(dist); +for i=1:Ndist + if strcmp(lower(dist{i}.type),'sample') + figure; + plot(dist{i}.P'); + xlabel('Sample'); + ylabel('Parameters'); + title(sprintf('%s trajectories',name)); + end +end + +Np=length(dist{1}.Ep); +try + order=dist{1}.order; + Np=length(order); +catch + order=[1:Np]; +end + +% Plot univariate densities +rNp=ceil(sqrt(Np)); +h=figure; +set(h,'Name',name); +for j=1:Np, + subplot(rNp,rNp,j); + jp=order(j); + for i=1:Ndist, + mci_plot_dist(dist{i},jp); + hold on + end + yl=get(gca,'YLim'); + if nargin > 2 + plot([P(jp),P(jp)],[0,yl(2)],'r','LineWidth',2); + end +end +if nargin > 2 + disp('True parameters shown in red'); +end + +plot_bivariate=0; +if plot_bivariate + % Plot bivariate densities + rNp=ceil(sqrt(Np)); + h=figure; + set(h,'Name',name); + k=1; + q=dist{1}.P(:,dist{1}.ind); + for i=1:Np, + for j=1:Np, + if j > i + subplot(Np,Np,k); + plot(q(i,:),q(j,:),'k.'); + xlabel(dist{1}.names{i}); + ylabel(dist{1}.names{j}); + hold on + plot(P(i),P(j),'ro'); + end + k=k+1; + end + end + if nargin > 2 + disp('True parameters shown in red'); + end + + % Posterior Correlation Matrix + for i=1:Ndist, + if strcmp(lower(dist{i}.type),'sample') + disp(' '); + disp(sprintf('Distribution %d',i)); + disp('Posterior Correlation Matrix:'); + corrcoef(dist{i}.P(:,dist{i}.ind)') + end + end +end diff --git a/toolbox/mci/plotting/mci_plot_noiseSD.m b/toolbox/mci/plotting/mci_plot_noiseSD.m new file mode 100644 index 00000000..f2ff6a2f --- /dev/null +++ b/toolbox/mci/plotting/mci_plot_noiseSD.m @@ -0,0 +1,28 @@ +function [] = mci_plot_noiseSD (Ce,ind) +% Plot posterior over observation noise SDs +% FORMAT [] = mci_plot_noiseSD (Ce,ind) +% +% Ce [d x d x Ns] d-dimensional observations with Ns samples +% ind indices of samples to plot +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_plot_noiseSD.m 6548 2015-09-11 12:39:47Z will $ + +d=size(Ce,1); +dist.ind=ind; +for j=1:d, + dist.P(j,:)=sqrt(squeeze(Ce(j,j,:))'); + dist.names{j}=sprintf('NoiseSD (%d)',j); +end +dist.color='k'; +dist.type='sample'; +dist.ks=1; +figure +rd=ceil(sqrt(d)); +for j=1:d, + subplot(rd,rd,j); + mci_plot_dist(dist,j); + grid on +end diff --git a/toolbox/mci/plotting/mci_plot_outputs.m b/toolbox/mci/plotting/mci_plot_outputs.m new file mode 100644 index 00000000..2f3aadc2 --- /dev/null +++ b/toolbox/mci/plotting/mci_plot_outputs.m @@ -0,0 +1,22 @@ +function [] = mci_plot_outputs (M,G) +% Plot outputs +% FORMAT [] = mci_plot_outputs (M,G) +% +% M Model +% G Data +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_plot_outputs.m 6548 2015-09-11 12:39:47Z will $ + +lw=2; +h=figure; +set(h,'Name','Outputs'); +plot(M.t,G,'LineWidth',lw); +for i=1:M.l, + outstr{i}=sprintf('y(%d)',i); +end +legend(outstr); +xlabel('Time'); +grid on \ No newline at end of file diff --git a/toolbox/mci/plotting/mci_plot_surface.m b/toolbox/mci/plotting/mci_plot_surface.m new file mode 100644 index 00000000..aaad0f75 --- /dev/null +++ b/toolbox/mci/plotting/mci_plot_surface.m @@ -0,0 +1,98 @@ +function [log_prob,S,E] = mci_plot_surface (P,M,U,Y,S,dist) +% Plot log probability surface +% FORMAT [log_prob,S,E] = mci_plot_surface (P,M,U,Y,S,dist) +% +% P Parameters +% M Model structure +% U Inputs +% Y Data +% S Surface data structure +% dist 'prior', 'like' or 'post' +%__________________________________________________________________________ +% Copyright (C) 2015 Wellcome Trust Centre for Neuroimaging + +% Will Penny +% $Id: mci_plot_surface.m 6548 2015-09-11 12:39:47Z will $ + +try + S.surf; +catch + S.surf=1; +end + +try + S.image; +catch + S.image=1; +end + +% Number of bins defining surface in each dimension +Nbins=S.Nbins; +pxy=S.pxy; + +[bup,bdown] = meshgrid(pxy(1,:),pxy(2,:)); + +% For computing log prior term +M = spm_mci_priors (M); +M = spm_mci_minit (M); + +% Which params to vary +P1str=[S.param{1},'=P1;']; +P2str=[S.param{2},'=P2;']; + +[I,J]=size(bup); +mm=1; +for i=1:I, + for j=1:J, + %disp(sprintf('Model %d out of %d',mm,I*J)); + P1=bup(i,j); + P2=bdown(i,j); + eval(P1str); + eval(P2str); + + % Get parameters in eigenspace of prior + Pv = spm_vec(P); + M.vpE=spm_vec(M.pE); + p = M.V'*(Pv-M.vpE); + switch lower(dist), + case 'post', + log_prob(i,j)= spm_mci_joint (p,M,U,Y); + + case 'prior', + log_prob(i,j)=-p'*M.ipC*p/2 + M.log_prior_t2; + + case 'like', + log_prob(i,j)= feval(M.L,Pv,M,U,Y); + + otherwise + disp('Unknown distribution type'); + return + end + mm=mm+1; + end +end + +if S.surf + figure + surf(bup,bdown,log_prob); + set(gca,'FontSize',18); + xlabel(S.name{1}); + ylabel(S.name{2}); + title(['log ',dist]); +end + +if S.image + figure; + imagesc(pxy(1,:),pxy(2,:),log_prob); + set(gca,'FontSize',18); + axis xy + hold on + xlabel(S.name{1}); + ylabel(S.name{2}); + title(['log ',dist]); +end + +S.x=bup; +S.y=bdown; +S.pxy=pxy; + diff --git a/toolbox/spectral/spm_dtf2gew.m b/toolbox/spectral/spm_dtf2gew.m index 83093cba..d9825c7d 100644 --- a/toolbox/spectral/spm_dtf2gew.m +++ b/toolbox/spectral/spm_dtf2gew.m @@ -1,5 +1,5 @@ function [gew,pve] = spm_dtf2gew(dtf,C) -% Convert cross sspectral density to Geweke Granger causality +% Converts directed transfer function to Geweke Granger causality % FORMAT [gew,pve] = spm_csd2gew(dtf,C) % % dtf (N,n,n) - (unnormalised) directed or modulation functions @@ -18,7 +18,7 @@ % Copyright (C) 2014 Wellcome Trust Centre for Neuroimaging % Karl Friston -% $Id: spm_dtf2gew.m 5908 2014-03-05 20:31:57Z karl $ +% $Id: spm_dtf2gew.m 6481 2015-06-16 17:01:47Z karl $ % preliminaries diff --git a/toolbox/spectral/spm_mar2ccf.m b/toolbox/spectral/spm_mar2ccf.m index e8401a37..c20cb21e 100644 --- a/toolbox/spectral/spm_mar2ccf.m +++ b/toolbox/spectral/spm_mar2ccf.m @@ -20,7 +20,7 @@ % Copyright (C) 2014 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_mar2ccf.m 5893 2014-02-24 12:44:17Z guillaume $ +% $Id: spm_mar2ccf.m 6481 2015-06-16 17:01:47Z karl $ % Nyquist @@ -75,7 +75,7 @@ C = spm_cat(C); K = inv(B - A); -% compute crosscovariance matrices and reduces to an array of vectors +% compute cross-covariance matrices and reduces to an array of vectors %-------------------------------------------------------------------------- CCF = K*C*K'; ccf = zeros(n,d,d); diff --git a/toolbox/spectral/spm_mar2csd.m b/toolbox/spectral/spm_mar2csd.m index 6a86c6f0..ea0fb7d1 100644 --- a/toolbox/spectral/spm_mar2csd.m +++ b/toolbox/spectral/spm_mar2csd.m @@ -24,7 +24,7 @@ % Copyright (C) 2008 Wellcome Trust Centre for Neuroimaging % Will Penny -% $Id: spm_mar2csd.m 6120 2014-07-24 11:20:12Z guillaume $ +% $Id: spm_mar2csd.m 6560 2015-09-23 13:50:43Z karl $ % Nyquist @@ -39,7 +39,6 @@ if isnumeric(mar) d = size(mar,2); p = size(mar,1)/d; - lag = cell(p,1); for i = 1:d for j = 1:d for k = 1:p @@ -47,6 +46,7 @@ end end end + clear mar mar.lag = lag; else d = length(mar.lag(1).a);