Specifying contrasts in SPM with nipype, unsure about session_list argument

This question stems from both not quite following the nipype interface to spm and also not being sure about elements of the SPM struct.

I’m trying to calculate t-scores with SPM via nipype using only a few of the available sessions. As I understand EstimateContrast, the following ought to define three contrasts that will look for beta values from either the first (cont0), second (cont1), or third (cont2) run in a dataset.

import os
import nipype.interfaces.spm as spm

est = spm.EstimateContrast()

cont0 = ('Task-Odd', 'T', ['Task-Odd','Task-Even'], [1.0, 0.], [0.])
cont1 = ('Task-Odd', 'T', ['Task-Odd','Task-Even'], [1.0, 0.], [1.])
cont2 = ('Task-Odd', 'T', ['Task-Odd','Task-Even'], [1.0, 0.], [2.])

contrasts = [cont0, cont1, cont2]
est.inputs.contrasts = contrasts

# _make_matlab_command needs an SPM.mat file to run
# make a temporary one here
os.system('touch SPM.mat')
est.inputs.spm_mat_file = 'SPM.mat'

cmd = est._make_matlab_command(_)

# write out newly generated script
text_file = open("output.m", "w")
text_file.write(cmd)
text_file.close()

# print the script to console
os.system('cat output.m')

The resulting script is

% generated by nipype.interfaces.spm
spm_defaults;
jobs{1}.stats{1}.con.spmmat  = {'SPM.mat'};
load(jobs{1}.stats{1}.con.spmmat{:});
SPM.swd = '/home/psadil/Desktop';
save(jobs{1}.stats{1}.con.spmmat{:},'SPM');
names = SPM.xX.name;
pat = 'Sn\([0-9]*\) (.*)\*bf\(1\)|Sn\([0-9]*\) .*\*bf\([2-9]\)|Sn\([0-9]*\) (.*)';
t = regexp(names,pat,'tokens');
pat1 = 'Sn\(([0-9].*)\)\s.*';
t1 = regexp(names,pat1,'tokens');
for i0=1:numel(t),condnames{i0}='';condsess(i0)=0;if ~isempty(t{i0}{1}),condnames{i0} = t{i0}{1}{1};condsess(i0)=str2num(t1{i0}{1}{1});end;end;
consess{1}.tcon.name   = 'Task-Odd';
consess{1}.tcon.convec = zeros(1,numel(names));
idx = strmatch('Task-Odd',condnames,'exact');
if isempty(idx), throw(MException('CondName:Chk', sprintf('Condition %s not found in design','Task-Odd'))); end;
sidx = find(condsess(idx)==1);
consess{1}.tcon.convec(idx(sidx)) = 0.000000;
idx = strmatch('Task-Even',condnames,'exact');
if isempty(idx), throw(MException('CondName:Chk', sprintf('Condition %s not found in design','Task-Even'))); end;
sidx = find(condsess(idx)==1);
consess{1}.tcon.convec(idx(sidx)) = 0.000000;
consess{2}.tcon.name   = 'Task-Odd';
consess{2}.tcon.convec = zeros(1,numel(names));
idx = strmatch('Task-Odd',condnames,'exact');
if isempty(idx), throw(MException('CondName:Chk', sprintf('Condition %s not found in design','Task-Odd'))); end;
sidx = find(condsess(idx)==1);
consess{2}.tcon.convec(idx(sidx)) = 1.000000;
idx = strmatch('Task-Even',condnames,'exact');
if isempty(idx), throw(MException('CondName:Chk', sprintf('Condition %s not found in design','Task-Even'))); end;
sidx = find(condsess(idx)==1);
consess{2}.tcon.convec(idx(sidx)) = 0.000000;
consess{3}.tcon.name   = 'Task-Odd';
consess{3}.tcon.convec = zeros(1,numel(names));
idx = strmatch('Task-Odd',condnames,'exact');
if isempty(idx), throw(MException('CondName:Chk', sprintf('Condition %s not found in design','Task-Odd'))); end;
sidx = find(condsess(idx)==1);
consess{3}.tcon.convec(idx(sidx)) = 2.000000;
idx = strmatch('Task-Even',condnames,'exact');
if isempty(idx), throw(MException('CondName:Chk', sprintf('Condition %s not found in design','Task-Even'))); end;
sidx = find(condsess(idx)==1);
consess{3}.tcon.convec(idx(sidx)) = 0.000000;
jobs{1}.stats{1}.con.consess = consess;
if strcmp(spm('ver'),'SPM8'), spm_jobman('initcfg');jobs=spm_jobman('spm5tospm8',{jobs});end
spm_jobman('run',jobs);Out[2]: 0

I would have assumed that the lines like sidx = find(condsess(idx)==1); determine which session to include, except, that sidx is always set to find places where condsess==1. Likewise, rather than the contrast vector being filled with 0s and 1s, it’s filled with 0 for the first contrast, 0 or 1 for the second, and 0 or 2 for the third.

I’m asking because the contrast runner is failing to run on a real dataset (part of the error copied below), and I’m wondering whether the weights and session list have ended up mixed.


SPM12 – 7487
MATLAB – 2016b
nipype – 1.2.0
python – 3.6.7
Ubuntu – 16.04.06


Standard error:
MATLAB code threw an exception:
Job execution failed. The full log of this run can be found in MATLAB command window, starting with the lines (look for the line showing the exact #job as displayed in this error message)

Running job #1

File:
Name:MATLABbatch system
Line:0
Return code: 0

190620-08:13:18,329 nipype.workflow INFO:
[MultiProc] Running 1 tasks, and 0 jobs ready. Free memory (GB): 13.90/14.10, Free processors: 6/7.
Currently running:
* decod.conWorkFlow.level1conest
190620-08:13:19,249 nipype.workflow WARNING:
[Node] Error on “decod.conWorkFlow.level1conest” (/home/vm01/Documents/fmri/VTF-high_data/derivatives/work/decod/conWorkFlow/_sub_02/_sdthresh_0.5/_run_0/level1conest)
190620-08:13:20,317 nipype.workflow ERROR:
Node level1conest.a0.b0.a0 failed to run on host ubuntu.
190620-08:13:20,317 nipype.workflow ERROR:
Saving crash info to /home/vm01/Documents/fmri/VTF-high_data/crash-20190620-081320-vm01-level1conest.a0.b0.a0-96722285-5d6d-489f-aecf-c9bd01e97f19.pklz
Traceback (most recent call last):
File “/home/vm01/Documents/fmri/nipype/nipype/pipeline/plugins/multiproc.py”, line 69, in run_node
result[‘result’] = node.run(updatehash=updatehash)
File “/home/vm01/Documents/fmri/nipype/nipype/pipeline/engine/nodes.py”, line 473, in run
result = self._run_interface(execute=True)
File “/home/vm01/Documents/fmri/nipype/nipype/pipeline/engine/nodes.py”, line 557, in _run_interface
return self._run_command(execute)
File “/home/vm01/Documents/fmri/nipype/nipype/pipeline/engine/nodes.py”, line 637, in _run_command
result = self._interface.run(cwd=outdir)
File “/home/vm01/Documents/fmri/nipype/nipype/interfaces/base/core.py”, line 375, in run
runtime = self._run_interface(runtime)
File “/home/vm01/Documents/fmri/nipype/nipype/interfaces/spm/base.py”, line 376, in _run_interface
results = self.mlab.run()
File “/home/vm01/Documents/fmri/nipype/nipype/interfaces/base/core.py”, line 375, in run
runtime = self._run_interface(runtime)
File “/home/vm01/Documents/fmri/nipype/nipype/interfaces/matlab.py”, line 170, in _run_interface
self.raise_exception(runtime)
File “/home/vm01/Documents/fmri/nipype/nipype/interfaces/base/core.py”, line 695, in raise_exception
).format(**runtime.dictcopy()))
RuntimeError: Command:
matlab -nodesktop -nosplash -singleCompThread -r “addpath(’/home/vm01/Documents/fmri/VTF-high_data/derivatives/work/decod/conWorkFlow/_sub_02/_sdthresh_0.5/_run_0/level1conest’);pyscript_estimatecontrast;exit”
Standard output:
MATLAB is selecting SOFTWARE OPENGL rendering.

                                                                               < M A T L A B (R) >
                                                                     Copyright 1984-2016 The MathWorks, Inc.
                                                                     R2016b (9.1.0.441655) 64-bit (glnxa64)
                                                                                September 7, 2016

To get started, type one of these: helpwin, helpdesk, or demo.
For product information, visit www.mathworks.com.

Executing pyscript_estimatecontrast at 20-Jun-2019 08:11:56:

MATLAB Version: 9.1.0.441655 (R2016b)
MATLAB License Number: 123456
Operating System: Linux 4.15.0-51-generic #55~16.04.1-Ubuntu SMP Thu May 16 09:24:37 UTC 2019 x86_64
Java Version: Java 1.7.0_60-b19 with Oracle Corporation Java HotSpot™ 64-Bit Server VM mixed mode

MATLAB Version 9.1 (R2016b)
Simulink Version 8.8 (R2016b)
Aerospace Blockset Version 3.18 (R2016b)
Aerospace Toolbox Version 2.18 (R2016b)
Antenna Toolbox Version 2.1 (R2016b)
Audio System Toolbox Version 1.1 (R2016b)
Bioinformatics Toolbox Version 4.7 (R2016b)
Communications System Toolbox Version 6.3 (R2016b)
Computer Vision System Toolbox Version 7.2 (R2016b)
Control System Toolbox Version 10.1 (R2016b)
Curve Fitting Toolbox Version 3.5.4 (R2016b)
DO Qualification Kit Version 3.2 (R2016b)
DSP System Toolbox Version 9.3 (R2016b)
Database Toolbox Version 7.0 (R2016b)
Datafeed Toolbox Version 5.4 (R2016b)
Econometrics Toolbox Version 3.5 (R2016b)
Embedded Coder Version 6.11 (R2016b)
FieldTrip Version unknown www.fieldtriptoolbox.org
Filter Design HDL Coder Version 3.1 (R2016b)
Financial Instruments Toolbox Version 2.4 (R2016b)
Financial Toolbox Version 5.8 (R2016b)
Fixed-Point Designer Version 5.3 (R2016b)
Fuzzy Logic Toolbox Version 2.2.24 (R2016b)
Global Optimization Toolbox Version 3.4.1 (R2016b)
HDL Coder Version 3.9 (R2016b)
HDL Verifier Version 5.1 (R2016b)
IEC Certification Kit Version 3.8 (R2016b)
Image Acquisition Toolbox Version 5.1 (R2016b)
Image Processing Toolbox Version 9.5 (R2016b)
Instrument Control Toolbox Version 3.10 (R2016b)
LTE System Toolbox Version 2.3 (R2016b)
MATLAB Coder Version 3.2 (R2016b)
MATLAB Compiler Version 6.3 (R2016b)
MATLAB Compiler SDK Version 6.3 (R2016b)
MATLAB Distributed Computing Server Version 6.9 (R2016b)
MATLAB Report Generator Version 5.1 (R2016b)
Mapping Toolbox Version 4.4 (R2016b)
Model Predictive Control Toolbox Version 5.2.1 (R2016b)
Neural Network Toolbox Version 9.1 (R2016b)
Optimization Toolbox Version 7.5 (R2016b)
Parallel Computing Toolbox Version 6.9 (R2016b)
Partial Differential Equation Toolbox Version 2.3 (R2016b)
Phased Array System Toolbox Version 3.3 (R2016b)
Polyspace Bug Finder Version 2.2 (R2016b)
Polyspace Code Prover Version 9.6 (R2016b)
RF Toolbox Version 3.1 (R2016b)
Risk Management Toolbox Version 1.0 (R2016b)
Robotics System Toolbox Version 1.3 (R2016b)
Robust Control Toolbox Version 6.2 (R2016b)
Signal Processing Toolbox Version 7.3 (R2016b)
SimBiology Version 5.5 (R2016b)
SimEvents Version 5.1 (R2016b)
SimRF Version 5.1 (R2016b)
Simscape Version 4.1 (R2016b)
Simscape Driveline Version 2.11 (R2016b)
Simscape Electronics Version 2.10 (R2016b)
Simscape Fluids Version 2.1 (R2016b)
Simscape Multibody Version 4.9 (R2016b)
Simscape Power Systems Version 6.6 (R2016b)
Simulink 3D Animation Version 7.6 (R2016b)
Simulink Code Inspector Version 2.6 (R2016b)
Simulink Coder Version 8.11 (R2016b)
Simulink Control Design Version 4.4 (R2016b)
Simulink Design Optimization Version 3.1 (R2016b)
Simulink Design Verifier Version 3.2 (R2016b)
Simulink Report Generator Version 5.1 (R2016b)
Simulink Test Version 2.1 (R2016b)
Simulink Verification and Validation Version 3.12 (R2016b)
Stateflow Version 8.8 (R2016b)
Statistical Parametric Mapping Version 7487 (SPM12)
Statistics and Machine Learning Toolbox Version 11.0 (R2016b)
Symbolic Math Toolbox Version 7.1 (R2016b)
System Identification Toolbox Version 9.5 (R2016b)
Trading Toolbox Version 3.1 (R2016b)
Vision HDL Toolbox Version 1.3 (R2016b)
WLAN System Toolbox Version 1.2 (R2016b)
Wavelet Toolbox Version 4.17 (R2016b)
Yokogawa MEG Reader toolbox for MATLAB Version 1.5.1
Direct calls to spm_defauts are deprecated.
Please use spm(‘Defaults’,modality) or spm_get_defaults instead.
Initialising batch system… done.


20-Jun-2019 08:12:54 - Running job #1

20-Jun-2019 08:12:54 - Running ‘Contrast Manager’

SPM12: spm_contrasts.m 08:12:54 - 20/06/2019

Contrasts folder : ./_run_0/level1conest
20-Jun-2019 08:13:15 - Failed ‘Contrast Manager’
Error using *
Inner matrix dimensions must agree.
In file “/home/vm01/matlab/toolbox/spm12/spm_FcUtil.m” (v5219), function “sf_X1o” at line 742.
In file “/home/vm01/matlab/toolbox/spm12/spm_FcUtil.m” (v5219), function “spm_FcUtil” at line 301.
In file “/home/vm01/matlab/toolbox/spm12/spm_contrasts.m” (v7029), function “spm_contrasts” at line 86.
In file “/home/vm01/matlab/toolbox/spm12/config/spm_run_con.m” (v7093), function “spm_run_con” at line 277.

The following modules did not run:
Failed: Contrast Manager

Standard error:
MATLAB code threw an exception:
Job execution failed. The full log of this run can be found in MATLAB command window, starting with the lines (look for the line showing the exact #job as displayed in this error message)

Running job #1

File:
Name:MATLABbatch system
Line:0
Return code: 0

190620-08:13:20,333 nipype.workflow INFO:
[MultiProc] Running 0 tasks, and 0 jobs ready. Free memory (GB): 14.10/14.10, Free processors: 7/7.
190620-08:13:22,311 nipype.workflow INFO:
***********************************
190620-08:13:22,311 nipype.workflow ERROR:
could not run node: decod.conWorkFlow.level1conest.a1.b0.a0
190620-08:13:22,312 nipype.workflow INFO:
crashfile: /home/vm01/Documents/fmri/VTF-high_data/crash-20190620-081318-vm01-level1conest.a1.b0.a0-213c1d1c-2b1f-488b-a444-727a931ef994.pklz
190620-08:13:22,312 nipype.workflow ERROR:
could not run node: decod.conWorkFlow.level1conest.a0.b0.a0
190620-08:13:22,312 nipype.workflow INFO:
crashfile: /home/vm01/Documents/fmri/VTF-high_data/crash-20190620-081320-vm01-level1conest.a0.b0.a0-96722285-5d6d-489f-aecf-c9bd01e97f19.pklz
190620-08:13:22,312 nipype.workflow INFO:
***********************************
Traceback (most recent call last):
File “/home/vm01/Documents/fmri/VTF-high_data/code/workflows/model/level1_analysis_decod.py”, line 520, in
decod.run(‘MultiProc’, plugin_args={‘n_procs’: 7})
File “/home/vm01/Documents/fmri/nipype/nipype/pipeline/engine/workflows.py”, line 599, in run
runner.run(execgraph, updatehash=updatehash, config=self.config)
File “/home/vm01/Documents/fmri/nipype/nipype/pipeline/plugins/base.py”, line 191, in run
report_nodes_not_run(notrun)
File “/home/vm01/Documents/fmri/nipype/nipype/pipeline/plugins/tools.py”, line 82, in report_nodes_not_run
raise RuntimeError(('Workflow did not execute cleanly. ’
RuntimeError: Workflow did not execute cleanly. Check log for details

Nevermind! I was just using session_list wrong. To do what I want, session_list should instead be a list with length equal to the number of sessions, with 1. for sessions to include and 0. elsewhere. I assume it’s useful to have it accept arguments like this so that different sessions can be given different weight in the contrast. I think the following would work. I think the following defines three separate contrasts, each of which uses either betas from only the first, second, or third session.

est = spm.EstimateContrast()

cont0 = ('Task-Odd', 'T', ['Task-Odd','Task-Even'], [1.0, 0.], [1., 0., 0.])
cont1 = ('Task-Odd', 'T', ['Task-Odd','Task-Even'], [1.0, 0.], [0., 1., 0.])
cont2 = ('Task-Odd', 'T', ['Task-Odd','Task-Even'], [1.0, 0.], [0., 0., 1.])

contrasts = [cont0, cont1, cont2]
est.inputs.contrasts = contrasts

# _make_matlab_command needs an SPM.mat file to run
# make a temporary one here
os.system('touch SPM.mat')
est.inputs.spm_mat_file = 'SPM.mat'

cmd = est._make_matlab_command(_)

# write out newly generated script
text_file = open("output.m", "w")
text_file.write(cmd)
text_file.close()

# print the script to console
os.system('cat output.m')