Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function.
The 21 matches · 2 of them tie a paragraph to a whole file, not to given lines: weak matches, whose lines are not tinted
- [1] § Results › Brain-age gap is associated with lifestyle factors and cognitive functions ↔ figures/IMAG2026/plot_sig_voxel.ipynb, lines 43–55 · score 0.83 · fluid intelligence, sleep duration, spent watching, physical exercise, correctly identify matches, principal component
- [2] § Methods › Brain-phenotype prediction ↔ figures/IMAG2026/compare_mmiva_msiva_sz.ipynb, lines 75–119 · score 0.77 · sex classification, diagnosis classification, Age regression, stratified, SVM, ridge
- [3] § Methods › Brain-phenotype prediction ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 269–311 · score 0.77 · sex classification, diagnosis classification, Age regression, stratified, SVM, ridge
- [4] § Methods › Multimodal subspace independent vector analysis › Alternating combinatorial and numerical optimization ↔ @utils/mymvlap.m, the whole file · a weak match · score 0.76 · dispersion matrix, positive definite, Laplace distribution, correlation matrix, covariance, dimensionality
- [5] § Results › Brain-age gap is associated with lifestyle factors and cognitive functions ↔ figures/IMAG2026/plot_sig_voxel.ipynb, lines 43–55 · score 0.72 · fluid intelligence, sleep duration, spent watching, physical exercise, voxels
- [6] § Methods › Multimodal subspace independent vector analysis › Alternating combinatorial and numerical optimization ↔ @utils/mymvk.m, the whole file · a weak match · score 0.71 · dispersion matrix, positive definite, correlation matrix, gamma, Laplace, covariance
- [7] § Methods › Datasets › Neuroimaging data ↔ figures/IMAG2026/plot_img_ukb.ipynb, lines 297–347 · score 0.68 · standard deviation, age median, sMRI, fMRI
- [8] § Methods › Experiments › Neuroimaging data experiment ↔ figures/IMAG2026/utils.py, lines 102–125 · score 0.62 · randomized dependence coefficient, correlation coefficient, RDC, nonlinear
- [9] § Results › MSIVA reveals linked phenotypic and neuropsychiatric biomarkers ↔ figures/IMAG2026/compare_mmiva_msiva_sz.ipynb, lines 75–119 · score 0.61 · age regression MAE, sex classification, diagnosis classification, accuracy, SZ, MSIVA
- [10] § Results › MSIVA reveals linked phenotypic and neuropsychiatric biomarkers ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 269–311 · score 0.61 · age regression MAE, sex classification, diagnosis classification, accuracy, SZ, subspace
- [11] § Results › MSIVA detects latent subspace structures in neuroimaging data ↔ figures/IMAG2026/plot_img_ukb_rdc.ipynb, lines 121–180 · score 0.60 · modal RDC, sMRI, unimodal initialization, CMCCs, multimodal initialization, CMDs
- [12] § Results › MSIVA detects latent subspace structures in neuroimaging data ↔ figures/IMAG2026/plot_img_sz_rdc.ipynb, lines 121–180 · score 0.58 · modal RDC, sMRI, unimodal initialization, CMCCs, multimodal initialization, CMDs
- [13] § Results › MSIVA detects latent subspace structures in neuroimaging data ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 133–192 · score 0.58 · cross modal Pearson, sMRI, fMRI, unimodal initialization, CMCCs, multimodal initialization
- [14] § Results › MSIVA detects latent subspace structures in neuroimaging data ↔ figures/IMAG2026/plot_img_ukb.ipynb, lines 133–192 · score 0.58 · cross modal Pearson, sMRI, fMRI, unimodal initialization, CMCCs, multimodal initialization
- [15] § Methods › Quantitative evaluation metrics › Mean correlation coefficient and minimum distance ↔ figures/IMAG2026/plot_loss.ipynb, lines 120–264 · score 0.58 · min max, MMCC, aggregated, CMCC, CMD, MMD
- [16] § Methods › Multimodal subspace independent vector analysis › Alternating combinatorial and numerical optimization ↔ other_methods/jbd.m, lines 1–147 · score 0.58 · Kullback Leibler, divergence, algorithm, latent, joint
- [17] § Methods › Multimodal subspace independent vector analysis › Alternating combinatorial and numerical optimization ↔ @MISAK/MISAK.m, lines 29–122 · score 0.57 · objective function, combinatorial optimization, greedy, Kotz, gradient, joint
- [18] § Results › MSIVA identifies ground-truth subspace structures in synthetic data ↔ figures/IMAG2026/plot_loss.ipynb, lines 11–118 · score 0.57 · numerical optimization, initialization workflow, MMCC, lowest, CMCC, CMD
- [19] § Results › MSIVA reveals linked phenotypic and neuropsychiatric biomarkers ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 539–602 · score 0.56 · older patients, cross modal correlations, 1–3, SZ, age, subspace
- [20] § Methods › Datasets › Neuroimaging data ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 313–362 · score 0.54 · age median, sMRI, fMRI
- [21] § Results › MSIVA reveals linked phenotypic and neuropsychiatric biomarkers ↔ figures/IMAG2026/plot_img_sz.ipynb, lines 539–602 · score 0.52 · older patient, Younger control, diagnosis, median, age, subspace
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Jupyter notebook · 622 lines · 26 KB · Apache-2.0 · 6 matches
- # %%
- import os
- import numpy as np
- import scipy.io as sio
- import seaborn as sns
- import matplotlib as mpl
- import matplotlib.pyplot as plt
- from matplotlib.colors import Normalize
- import ancillary as ac
- from sklearn.svm import LinearSVC
- from sklearn.linear_model import Ridge
- from sklearn.model_selection import GridSearchCV
- from sklearn.model_selection import train_test_split
- import hdmedians as hd
- from utils import correlation, calculate_mcc, age_regression, sex_classification, sz_classification, plot_sq, convert_pvalue, pvalue_to_r
- from scipy import stats
- from scipy.stats import pearsonr
- # %%
- res_dir = "/data/users4/xli/MSIVA/MSIVA/results"
- sz_smri_data_path = os.path.join(res_dir, "mat", "mancovaOuts_allHCSZ_combinedRelatives_wX_preregSite_C30_SMRI_GICAinit.mat")
- sz_smri_data = sio.loadmat(sz_smri_data_path)['NMODELHCSZ0ns']
- sz_smri_data_array = sz_smri_data[0][0][0]
- age = sz_smri_data_array[:,0]
- sex = sz_smri_data_array[:,1]
- diagnosis = sz_smri_data_array[:,2]
- id = sio.loadmat(os.path.join(res_dir, "mat", "SZID.mat"))['ID'][0] - 1
- # %%
- img_dir = os.path.join(res_dir, "img")
- subspace_struct_list = ["s1", "s2", "s3", "s4", "s5"]
- num_subspace_struct = len(subspace_struct_list)
- Y = np.zeros((num_subspace_struct,3,2,12,999)) # S1-4, UA/MSIVA/GICA, M1-2, voxel, source
- W = np.zeros((num_subspace_struct,3,2,12,44318)) # S1-4, UA/MSIVA/GICA, M1-2, voxel, source
- num_iter = 21
- corr = np.zeros((num_subspace_struct,9,12,12))
- for i,ss in enumerate(subspace_struct_list):
- data = sio.loadmat(os.path.join(img_dir, ss, "um_neuroimaging_sz_Y.mat"))
- Y1 = np.squeeze(data['Y1'])
- data = sio.loadmat(os.path.join(img_dir, ss, "ummm_neuroimaging_sz_Y.mat"))
- Y2 = np.squeeze(data['Y2'])
- data = sio.loadmat(os.path.join(img_dir, ss, "mm_neuroimaging_sz_Y.mat"))
- Y3 = np.squeeze(data['Y3'])
- Y[i,0,0] = Y1[0][:,id]
- Y[i,0,1] = Y1[1][:,id]
- Y[i,1,0] = Y2[0][:,id]
- Y[i,1,1] = Y2[1][:,id]
- Y[i,2,0] = Y3[0][:,id]
- Y[i,2,1] = Y3[1][:,id]
- # for j in range(2):
- # for k in range(2):
- # for l in range(12):
- # sgn = np.sign(correlation(Y[i,j,k,l],age))
- # Y[i,j,k,l] = -sgn * Y[i,j,k,l]
- data = sio.loadmat(os.path.join(img_dir, ss, "um_neuroimaging_sz_W.mat"))
- W1 = np.squeeze(data['W1'])
- data = sio.loadmat(os.path.join(img_dir, ss, "ummm_neuroimaging_sz_W.mat"))
- W2 = np.squeeze(data['W2'])
- data = sio.loadmat(os.path.join(img_dir, ss, "mm_neuroimaging_sz_W.mat"))
- W3 = np.squeeze(data['W3'])
- W[i,0,0] = W1[0]
- W[i,0,1] = W1[1]
- W[i,1,0] = W2[0]
- W[i,1,1] = W2[1]
- W[i,2,0] = W3[0]
- W[i,2,1] = W3[1]
- corr[i,0] = np.corrcoef(Y1[0],Y1[0])[:12,:12]
- corr[i,1] = np.corrcoef(Y1[1],Y1[1])[:12,:12]
- corr[i,2] = np.corrcoef(Y1[0],Y1[1])[12:,:12]
- corr[i,3] = np.corrcoef(Y2[0],Y2[0])[:12,:12]
- corr[i,4] = np.corrcoef(Y2[1],Y2[1])[:12,:12]
- corr[i,5] = np.corrcoef(Y2[0],Y2[1])[12:,:12]
- corr[i,6] = np.corrcoef(Y3[0],Y3[0])[:12,:12]
- corr[i,7] = np.corrcoef(Y3[1],Y3[1])[:12,:12]
- corr[i,8] = np.corrcoef(Y3[0],Y3[1])[12:,:12]
- # %%
- val = 1
- num_source = 12
- # S1
- num_unique_source = 3
- s1 = np.zeros((num_source, num_source))
- s1[:2,:2] = val
- s1[2:5,2:5] = val*2
- s1[5:9,5:9] = val*3
- # S2
- num_unique_source = 2
- s2 = np.zeros((num_source, num_source))
- s2[:2,:2] = val
- s2[2:4,2:4] = val*2
- s2[4:6,4:6] = val*3
- s2[6:8,6:8] = val*4
- s2[8:10,8:10] = val*5
- # S3
- num_unique_source = 3
- s3 = np.zeros((num_source, num_source))
- s3[:3,:3] = val
- s3[3:6,3:6] = val*2
- s3[6:9,6:9] = val*3
- # S4
- num_unique_source = 4
- s4 = np.zeros((num_source, num_source))
- s4[:4,:4] = val
- s4[4:8,4:8] = val*2
- # S5
- num_unique_source = 4
- s5 = np.zeros((num_source, num_source))
- for i in range(12):
- s5[i,i] = val*(i+1)
- s_list = [s1, s2, s3, s4, s5]
- # %%
- analysis_list = ["I. Unimodal initialization sMRI Pearson correlations",
- "II. Unimodal initialization fMRI Pearson correlations",
- "III. Unimodal initialization cross-modal Pearson correlations",
- "IV. Default initialization sMRI Pearson correlations",
- "V. Default initialization fMRI Pearson correlations",
- "VI. Default initialization cross-modal Pearson correlations",
- "VII. Multimodal initialization sMRI Pearson correlations",
- "VIII. Multimodal initialization fMRI Pearson correlations",
- "IX. Multimodal initialization cross-modal Pearson correlations"]
- modality_list = ["sMRI", "fMRI"]
- subspace_dict = {"S1": [2, 3, 4], "S2": [2, 2, 2, 2, 2], "S3": [3, 3, 3], "S4": [4, 4], "S5": [1]*12}
- n_row = 9
- fig, axes = plt.subplots(n_row, num_subspace_struct + 1, figsize = (2.4 * num_subspace_struct, 3 * n_row), gridspec_kw = {'width_ratios': [1, 1, 1, 1, 1, 0.05]})
- for i in range(num_subspace_struct):
- ss = subspace_dict[f"S{i+1}"]
- for j in range(n_row):
- ax = axes[j,i]
- abscorr = np.abs(corr[i,j])
- if j in [2, 5, 8]:
- mcc, md, aggcorr, _ = calculate_mcc(abscorr, ss, sort=False)
- sns.heatmap(abscorr, cmap="magma", vmin=0, vmax=1, ax=ax, cbar=False)
- ax.text(9.9, 1.6, f"$S_{i+1}^{{Test}}$", fontsize=18, color="white", ha="center", va="center")
- ax.set_title(f"CMCC:{mcc:.3f} CMD:{md:.3f}", fontsize=12)
- if i == 2:
- ax.set_xlabel("sMRI", fontsize=18)
- if i == 0:
- ax.set_ylabel("fMRI", fontsize=18, rotation=0, labelpad=20)
- plot_sq(ax, i, crossmodal=True)
- ax.set_xlim(-0.1, 12.1)
- ax.set_ylim(12.1, -0.1)
- else:
- mcc = np.mean(np.diag(abscorr))
- sns.heatmap(abscorr, cmap="magma", vmin=0, vmax=1, ax=ax, cbar=False)
- ax.text(9.9, 1.6, f"$S_{i+1}^{{Test}}$", fontsize=18, color="white", ha="center", va="center")
- if i == 2:
- ax.set_xlabel(f"{modality_list[j%3]}", fontsize=18)
- if i == 0:
- ax.set_ylabel(f"{modality_list[j%3]}", fontsize=18, rotation=0, labelpad=20)
- plot_sq(ax, i)
- ax.set_xlim(-0.1, 12.1)
- ax.set_ylim(12.1, -0.1)
- ax.set_xticks([])
- ax.set_yticks([])
- norm = mpl.colors.Normalize(vmin=0, vmax=1)
- sm = mpl.cm.ScalarMappable(cmap="magma", norm=norm)
- for i in range(n_row):
- ax = fig.add_subplot(n_row, 1, i+1)
- ax.set_title(analysis_list[i], fontsize=20, fontweight='bold', pad=28)
- ax.axis('off')
- cbar = fig.colorbar(sm, cax=axes[i, 5])
- cbar.ax.tick_params(labelsize=11)
- plt.tight_layout(pad=1, h_pad=0, w_pad=1)
- plt.savefig("figures/neuroimaging_sz.pdf")
- # %%
- A = sio.loadmat(os.path.join(res_dir, "mat", "A_sz.mat"))["A"]
- WAY_list = []
- cca_corr_list = []
- for i in np.arange(0,10,2):
- # S1-4, UA/MSIVA, M1-2, voxel, source
- A1 = A[1,1,0,:,i:i+2] # structure 2, MSIVA, M1
- A2 = A[1,1,1,:,i:i+2] # structure 2, MSIVA, M2
- Y1 = Y[1,1,0,i:i+2]
- Y2 = Y[1,1,1,i:i+2]
- AY1 = A1@Y1
- AY2 = A2@Y2
- # PCA AY1, AY2
- AY1_p, AY1_p_projM, AY1_p_projM_std = ac.base_PCA(AY1, num_PC=None, axis=-2, whitening=True)
- AY2_p, AY2_p_projM, AY2_p_projM_std = ac.base_PCA(AY2, num_PC=None, axis=-2, whitening=True)
- # Post-PCA eigenvalue problem for CCA
- S12 = AY1_p @ AY2_p.T
- Z1 = np.zeros((AY1_p.shape[0],AY1_p.shape[0]), dtype=AY1_p.dtype)
- Z2 = np.zeros((AY2_p.shape[0],AY2_p.shape[0]), dtype=AY2_p.dtype)
- J = np.block([[Z1, S12],[S12.T, Z2]])
- U, S = ac.do_cov_EVD(J, k=2) # here, k = smallest subspace size in each modality
- # Final transformations: these multiply AY
- W1 = U[:2,].T @ AY1_p_projM
- W2 = U[2:,].T @ AY2_p_projM
- WAY1 = W1 @ AY1
- WAY2 = W2 @ AY2
- WAY_list.append( [WAY1, WAY2] )
- cca_corr = np.corrcoef(WAY1, WAY2)[2:,0:2]
- cca_corr_list.append(cca_corr)
- # %%
- num_voxel = A.shape[3]
- num_crossmodal_subspace = 5
- voxelwise_cca_corr = np.zeros((num_crossmodal_subspace, num_voxel))
- for j, i in enumerate(np.arange(0,10,2)):
- A1 = A[1,1,0,:,i:i+2] # structure 2, MSIVA, M1
- A2 = A[1,1,1,:,i:i+2] # structure 2, MSIVA, M2
- Y1 = Y[1,1,0,i:i+2]
- Y2 = Y[1,1,1,i:i+2]
- AY1 = A1@Y1
- AY2 = A2@Y2
- for k in range(num_voxel):
- AY1_voxel = np.expand_dims(AY1[k, :], axis=0)
- AY2_voxel = np.expand_dims(AY2[k, :], axis=0)
- # PCA AY1, AY2
- AY1_p, AY1_p_projM, AY1_p_projM_std = ac.base_PCA(AY1_voxel, num_PC=None, axis=-2, whitening=True)
- AY2_p, AY2_p_projM, AY2_p_projM_std = ac.base_PCA(AY2_voxel, num_PC=None, axis=-2, whitening=True)
- # Post-PCA eigenvalue problem for CCA
- S12 = AY1_p @ AY2_p.T
- Z1 = np.zeros((AY1_p.shape[0],AY1_p.shape[0]), dtype=AY1_p.dtype)
- Z2 = np.zeros((AY2_p.shape[0],AY2_p.shape[0]), dtype=AY2_p.dtype)
- J = np.block([[Z1, S12], [S12.T, Z2]])
- U, S = ac.do_cov_EVD(J, k=2) # here, k = smallest subspace size in each modality
- # Final transformations: these multiply AY
- W1 = U[:1,].T @ AY1_p_projM
- W2 = U[1:,].T @ AY2_p_projM
- WAY1 = W1 @ AY1_voxel
- WAY2 = W2 @ AY2_voxel
- voxelwise_cca_corr[j, k] = np.corrcoef(WAY1, WAY2)[2:,0:2][0,0]
- # sio.savemat("voxelwise_cca_corr_sz.mat", {"corr": voxelwise_cca_corr})
- # %%
- regularizer_range = np.linspace(0.1, 1, 10)
- param_grid_rr = [{'alpha': regularizer_range}]
- param_grid_svm = [{'C': regularizer_range}]
- age_mae = np.zeros(5)
- age_coef = np.zeros((5, 4))
- diagnosis_acc = np.zeros(5)
- diagnosis_coef = np.zeros((5, 4))
- sex_acc = np.zeros(5)
- sex_coef = np.zeros((5, 4))
- for i in range(5):
- X12 = (np.concatenate((WAY_list[i][0], WAY_list[i][1]), axis=0)).T
- age_subset = age[age>15] # there is only one subject with age 15 and it can't be stratified
- X12_subset = X12[age>15, :]
- X_train, X_test, y_train, y_test = train_test_split(X12_subset, age_subset, test_size=0.3, random_state=42, stratify=age_subset)
- X_train = np.concatenate([X_train, X12[age==15, :]], axis=0)
- y_train = np.concatenate([y_train, np.array([15])], axis=0)
- base_estimator = Ridge()
- rr = GridSearchCV(base_estimator, param_grid_rr, cv=10).fit(X_train, y_train)
- mae, coef = age_regression(X_train, X_test, y_train, y_test, a=rr.best_params_['alpha'])
- age_mae[i] = mae
- age_coef[i] = coef
- X12_subset = X12[diagnosis<2, :]
- diagnosis_subset = diagnosis[diagnosis < 2]
- X_train, X_test, y_train, y_test = train_test_split(X12_subset, diagnosis_subset, test_size=0.3, random_state=42, stratify=diagnosis_subset)
- base_estimator = LinearSVC(dual=False)
- svc = GridSearchCV(base_estimator, param_grid_svm, cv=10).fit(X_train, y_train)
- acc, coef = sz_classification(X_train, X_test, y_train, y_test, c=svc.best_params_['C'])
- diagnosis_acc[i] = acc*100
- diagnosis_coef[i] = coef[0]
- X_train, X_test, y_train, y_test = train_test_split(X12, sex, test_size=0.3, random_state=42, stratify=sex)
- base_estimator = LinearSVC(dual=False)
- svc = GridSearchCV(base_estimator, param_grid_svm, cv=10).fit(X_train, y_train)
- acc, coef = sex_classification(X_train, X_test, y_train, y_test, c=svc.best_params_['C'])
- sex_acc[i] = acc*100
- sex_coef[i] = coef[0]
- print(f"Subspace {i+1}: age regression MAE {age_mae[i]:.3f}, diagnosis classification accuracy {diagnosis_acc[i]:.3f}, sex classification accuracy {sex_acc[i]:.3f}")
- # %%
- num_subject = len(age)
- age_median = np.median(age)
- cmap = plt.cm.jet
- norm = Normalize(vmin=age.min(), vmax=age.max())
- lim = 3.6
- fig, axes = plt.subplots(2,5,figsize=(12.5,5.8))
- for k in range(5):
- for i in range(2):
- WAY1 = WAY_list[k][0]
- WAY2 = WAY_list[k][1]
- sign1 = np.sign(correlation(WAY1,age))
- sign2 = np.sign(correlation(WAY2,age))
- for j in range(2):
- WAY1[j,:] = -sign1[j]*WAY1[j,:]
- WAY2[j,:] = -sign2[j]*WAY2[j,:]
- axes[i,k].set_aspect('equal', 'box')
- if k == 2:
- axes[i,k].set_xlabel('$\hat{\mathbf{p}}_k^\\top \hat{\mathbf{S}}_k^{[1]}$ (sMRI)', fontsize=16)
- if k == 0:
- axes[i,k].set_ylabel('$\hat{\mathbf{q}}_k^\\top \hat{\mathbf{S}}_k^{[2]}$ (fMRI)', fontsize=16)
- axes[i,k].set_title(f'Source {2*k+i+1}', fontsize=18)
- axes[i,k].set_xlim([-lim,lim])
- axes[i,k].set_ylim([-lim-0.4,lim-0.4])
- r = format(round(cca_corr_list[k][i,i],3), '.3f')
- stat1, p1 = stats.ttest_ind(WAY1[i][age<age_median], WAY1[i][age>=age_median])
- stat2, p2 = stats.ttest_ind(WAY2[i][age<age_median], WAY2[i][age>=age_median])
- p1_str = convert_pvalue(p1*20)
- p2_str = convert_pvalue(p2*20)
- if k == 4 and i == 1:
- axes[i,k].annotate(f'$r_{{10}}$={r}', xy=(58, 128), xycoords='axes points', size=11, ha='right', va='top')
- else:
- axes[i,k].annotate(f'$r_{2*k+i+1}$={r}', xy=(54, 128), xycoords='axes points', size=11, ha='right', va='top')
- axes[i,k].annotate(f'MAE={age_mae[k]:.3f}yr', xy=(128, 27), xycoords='axes points', size=11, ha='right', va='top')
- if k == 4 and i == 1:
- axes[i,k].annotate(f'$p^{{[1]}}_{{10}}${p1_str}, $p^{{[2]}}_{{10}}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
- else:
- axes[i,k].annotate(f'$p^{{[1]}}_{2*k+i+1}${p1_str}, $p^{{[2]}}_{2*k+i+1}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
- age_subplot = axes[i,k].scatter(WAY1[i], WAY2[i], c=age, cmap=cmap, norm=norm, marker='.', alpha=1)
- cbar_ax = fig.add_axes([1, 0.13, 0.012, 0.8])
- cbar = plt.colorbar(age_subplot, cax=cbar_ax)
- cbar.set_label('Age (yr)', fontsize=14)
- cbar.ax.tick_params(labelsize=12)
- plt.tight_layout()
- plt.savefig("figures/cca_age_sz.png", bbox_inches='tight', dpi=2000)
- # %%
- lim = 3.6
- fig, axes = plt.subplots(2,5,figsize=(12.5,5.8))
- for k in range(5):
- for i in range(2):
- WAY1 = WAY_list[k][0]
- WAY2 = WAY_list[k][1]
- sign1 = np.sign(correlation(WAY1,age))
- sign2 = np.sign(correlation(WAY2,age))
- for j in range(2):
- WAY1[j,:] = -sign1[j]*WAY1[j,:]
- WAY2[j,:] = -sign2[j]*WAY2[j,:]
- axes[i,k].set_aspect('equal', 'box')
- if k == 2:
- axes[i,k].set_xlabel('$\hat{\mathbf{p}}_k^\\top \hat{\mathbf{S}}_k^{[1]}$ (sMRI)', fontsize=16)
- if k == 0:
- axes[i,k].set_ylabel('$\hat{\mathbf{q}}_k^\\top \hat{\mathbf{S}}_k^{[2]}$ (fMRI)', fontsize=16)
- axes[i,k].set_title(f'Source {2*k+i+1}', fontsize=18)
- axes[i,k].set_xlim([-lim,lim])
- axes[i,k].set_ylim([-lim-0.4,lim-0.4])
- r = format(round(cca_corr_list[k][i,i],3), '.3f')
- stat1, p1 = stats.ttest_ind(WAY1[i][diagnosis==0], WAY1[i][diagnosis==1])
- stat2, p2 = stats.ttest_ind(WAY2[i][diagnosis==0], WAY2[i][diagnosis==1])
- p1_str = convert_pvalue(p1*20) # correct for number of subjects
- p2_str = convert_pvalue(p2*20)
- if k == 4 and i == 1:
- axes[i,k].annotate(f'$r_{{10}}$={r}', xy=(58, 128), xycoords='axes points', size=11, ha='right', va='top')
- else:
- axes[i,k].annotate(f'$r_{2*k+i+1}$={r}', xy=(54, 128), xycoords='axes points', size=11, ha='right', va='top')
- axes[i,k].annotate(f'Acc.={diagnosis_acc[k]:.3f}%', xy=(128, 27), xycoords='axes points', size=11, ha='right', va='top')
- if k == 4 and i == 1:
- axes[i,k].annotate(f'$p^{{[1]}}_{{10}}${p1_str}, $p^{{[2]}}_{{10}}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
- else:
- axes[i,k].annotate(f'$p^{{[1]}}_{2*k+i+1}${p1_str}, $p^{{[2]}}_{2*k+i+1}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
- if k==0 and i==0:
- axes[i,k].scatter(WAY1[i,diagnosis==0], WAY2[i,diagnosis==0],color=sns.color_palette("tab10")[0],marker='.',alpha=0.5,label="HC")
- axes[i,k].scatter(WAY1[i,diagnosis==1], WAY2[i,diagnosis==1],color=sns.color_palette("tab10")[1],marker='.',alpha=0.5,label="SZ")
- else:
- axes[i,k].scatter(WAY1[i,diagnosis==0], WAY2[i,diagnosis==0],color=sns.color_palette("tab10")[0],marker='.',alpha=0.5)
- axes[i,k].scatter(WAY1[i,diagnosis==1], WAY2[i,diagnosis==1],color=sns.color_palette("tab10")[1],marker='.',alpha=0.5)
- fig.legend(bbox_to_anchor=(1.07, 0.27), fontsize=14)
- plt.tight_layout()
- plt.savefig("figures/cca_diag_sz.png", bbox_inches='tight', dpi=2000)
- # %%
- lim = 3.6
- fig, axes = plt.subplots(2,5,figsize=(12.5,5.8))
- num_subject = len(age)
- for k in range(5):
- for i in range(2):
- WAY1 = WAY_list[k][0]
- WAY2 = WAY_list[k][1]
- sign1 = np.sign(correlation(WAY1,age))
- sign2 = np.sign(correlation(WAY2,age))
- for j in range(2):
- WAY1[j,:] = -sign1[j]*WAY1[j,:]
- WAY2[j,:] = -sign2[j]*WAY2[j,:]
- axes[i,k].set_aspect('equal', 'box')
- if k == 2:
- axes[i,k].set_xlabel('$\hat{\mathbf{p}}_k^\\top \hat{\mathbf{S}}_k^{[1]}$ (sMRI)', fontsize=16)
- if k == 0:
- axes[i,k].set_ylabel('$\hat{\mathbf{q}}_k^\\top \hat{\mathbf{S}}_k^{[2]}$ (fMRI)', fontsize=16)
- axes[i,k].set_title(f'Source {2*k+i+1}', fontsize=18)
- axes[i,k].set_xlim([-lim,lim])
- axes[i,k].set_ylim([-lim-0.4,lim-0.4])
- r = format(round(cca_corr_list[k][i,i],3), '.3f')
- stat1, p1 = stats.ttest_ind(WAY1[i][sex==0], WAY1[i][sex==1])
- stat2, p2 = stats.ttest_ind(WAY2[i][sex==0], WAY2[i][sex==1])
- p1_str = convert_pvalue(p1*20) # correct for number of subjects
- p2_str = convert_pvalue(p2*20)
- if k == 4 and i == 1:
- axes[i,k].annotate(f'$r_{{10}}$={r}', xy=(58, 128), xycoords='axes points', size=11, ha='right', va='top')
- else:
- axes[i,k].annotate(f'$r_{2*k+i+1}$={r}', xy=(54, 128), xycoords='axes points', size=11, ha='right', va='top')
- axes[i,k].annotate(f'Acc.={sex_acc[k]:.3f}%', xy=(128, 27), xycoords='axes points', size=11, ha='right', va='top')
- if k == 4 and i == 1:
- axes[i,k].annotate(f'$p^{{[1]}}_{{10}}${p1_str}, $p^{{[2]}}_{{10}}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
- else:
- axes[i,k].annotate(f'$p^{{[1]}}_{2*k+i+1}${p1_str}, $p^{{[2]}}_{2*k+i+1}${p2_str}', xy=(128, 16), xycoords='axes points', size=11, ha='right', va='top')
- if k==0 and i==0:
- axes[i,k].plot(WAY1[i][sex==0], WAY2[i][sex==0],'b.',alpha=0.3,label='M')
- axes[i,k].plot(WAY1[i][sex==1], WAY2[i][sex==1],'r.',alpha=0.3,label='F')
- else:
- axes[i,k].plot(WAY1[i][sex==0], WAY2[i][sex==0],'b.',alpha=0.3)
- axes[i,k].plot(WAY1[i][sex==1], WAY2[i][sex==1],'r.',alpha=0.3)
- fig.legend(bbox_to_anchor=(1.06, 0.27), fontsize=14)
- plt.tight_layout()
- plt.savefig("figures/cca_sex_sz.png", bbox_inches='tight', dpi=2000)
- # %%
- # M1-2, S1-5, median/young control/old control/young patient/old patient, voxel
- age_median = np.median(age)
- AY_median = np.zeros((2, 5, 5, 44318))
- for s, i in enumerate(np.arange(0,10,2)):
- for m in range(2):
- Am = A[1,1,m,:,i:i+2]
- Ym = Y[1,1,m,i:i+2]
- AYm = Am@Ym
- AYm_young_hc = AYm[:,(age<age_median)&(diagnosis==0)]
- AYm_old_hc = AYm[:,(age>=age_median)&(diagnosis==0)]
- AYm_young_sz = AYm[:,(age<age_median)&(diagnosis==1)]
- AYm_old_sz = AYm[:,(age>=age_median)&(diagnosis==1)]
- AYm_median = hd.geomedian(AYm,axis=1)
- AYm_young_hc_median = hd.geomedian(AYm_young_hc,axis=1)
- AYm_old_hc_median = hd.geomedian(AYm_old_hc,axis=1)
- AYm_young_sz_median = hd.geomedian(AYm_young_sz,axis=1)
- AYm_old_sz_median = hd.geomedian(AYm_old_sz,axis=1)
- AY_median[m,s,0] = AYm_median
- AY_median[m,s,1] = AYm_young_hc_median
- AY_median[m,s,2] = AYm_old_hc_median
- AY_median[m,s,3] = AYm_young_sz_median
- AY_median[m,s,4] = AYm_old_sz_median
- # sio.savemat(os.path.join(res_dir, "mat", "AY_median_sz_interaction.mat"), {"AY_median": AY_median})
- # %%
- # M1-2, S1-5, median/young control/old control/young patient/old patient, voxel
- n_voxel = 44318
- age_median = np.median(age)
- AY_median = np.zeros((2, 5, 5, n_voxel))
- for s, i in enumerate(np.arange(0,10,2)):
- A1 = A[1,1,0,:,i:i+2]
- Y1 = Y[1,1,0,i:i+2]
- A2 = A[1,1,1,:,i:i+2]
- Y2 = Y[1,1,1,i:i+2]
- AY1 = A1@Y1
- AY2 = A2@Y2
- AY = np.concatenate((AY1, AY2), axis=0)
- AYm_young_hc = AY[:,(age<age_median)&(diagnosis==0)]
- AYm_old_hc = AY[:,(age>=age_median)&(diagnosis==0)]
- AYm_young_sz = AY[:,(age<age_median)&(diagnosis==1)]
- AYm_old_sz = AY[:,(age>=age_median)&(diagnosis==1)]
- AYm_median = hd.geomedian(AY,axis=1)
- AYm_young_hc_median = hd.geomedian(AYm_young_hc,axis=1)
- AYm_old_hc_median = hd.geomedian(AYm_old_hc,axis=1)
- AYm_young_sz_median = hd.geomedian(AYm_young_sz,axis=1)
- AYm_old_sz_median = hd.geomedian(AYm_old_sz,axis=1)
- AY_median[0,s,0] = AYm_median[:n_voxel]
- AY_median[0,s,1] = AYm_young_hc_median[:n_voxel]
- AY_median[0,s,2] = AYm_old_hc_median[:n_voxel]
- AY_median[0,s,3] = AYm_young_sz_median[:n_voxel]
- AY_median[0,s,4] = AYm_old_sz_median[:n_voxel]
- AY_median[1,s,0] = AYm_median[n_voxel:]
- AY_median[1,s,1] = AYm_young_hc_median[n_voxel:]
- AY_median[1,s,2] = AYm_old_hc_median[n_voxel:]
- AY_median[1,s,3] = AYm_young_sz_median[n_voxel:]
- AY_median[1,s,4] = AYm_old_sz_median[n_voxel:]
- # sio.savemat(os.path.join(res_dir, "mat", "stacked_AY_median_sz_interaction.mat"), {"AY_median": AY_median})
- # %%
- age_median = np.median(age)
- AY_corr = np.zeros((5, 5, 44318))
- p_value = np.zeros((5, 5, 44318))
- for s, i in enumerate(np.arange(0,10,2)):
- A1 = A[1,1,0,:,i:i+2]
- Y1 = Y[1,1,0,i:i+2]
- A2 = A[1,1,1,:,i:i+2]
- Y2 = Y[1,1,1,i:i+2]
- AY1 = A1@Y1
- AY2 = A2@Y2
- for j in range(44318):
- AY_corr[s,0,j], p_value[s,0,j] = pearsonr(AY1[j], AY2[j])
- AY_corr[s,1,j], p_value[s,1,j] = pearsonr(AY1[j,(age<age_median)&(diagnosis==0)], AY2[j,(age<age_median)&(diagnosis==0)])
- AY_corr[s,2,j], p_value[s,2,j] = pearsonr(AY1[j,(age>=age_median)&(diagnosis==0)], AY2[j,(age>=age_median)&(diagnosis==0)])
- AY_corr[s,3,j], p_value[s,3,j] = pearsonr(AY1[j,(age<age_median)&(diagnosis==1)], AY2[j,(age<age_median)&(diagnosis==1)])
- AY_corr[s,4,j], p_value[s,4,j] = pearsonr(AY1[j,(age>=age_median)&(diagnosis==1)], AY2[j,(age>=age_median)&(diagnosis==1)])
- # sio.savemat(os.path.join(res_dir, "mat", "AY_group_corr_sz_interaction.mat"), {"AY_corr": AY_corr})
- # sio.savemat(os.path.join(res_dir, "mat", "AY_group_pvalue_sz_interaction.mat"), {"p_value": p_value})
- # %%
- # AY_corr = sio.loadmat("mat/AY_group_corr_sz_interaction.mat")["AY_corr"]
- age_median = np.median(age)
- n_young_control = np.sum((age<age_median)&(diagnosis==0))
- n_old_control = np.sum((age>=age_median)&(diagnosis==0))
- n_young_patient = np.sum((age<age_median)&(diagnosis==1))
- n_old_patient = np.sum((age>=age_median)&(diagnosis==1))
- n_list = [n_young_control, n_old_control, n_young_patient, n_old_patient]
- cmap = plt.get_cmap('jet')
- percentiles = [30, 70]
- norm_percentiles = [p / 100.0 for p in percentiles]
- colors = [cmap(norm) for norm in norm_percentiles]
- title_list = ["All", "Younger control", "Older control", "Younger patient", "Older patient"]
- group_list = ["control", "patient"]
- color_list = [colors, [sns.color_palette("tab10")[0], sns.color_palette("tab10")[1]]]
- for s in range(5):
- for i, j in enumerate([1,3]):
- fig, ax = plt.subplots(1, 1, figsize=(3, 3))
- pct_neg, pct_pos, h_pct_neg, h_pct_pos = [], [], [], []
- for k in range(2):
- fd = 2*(np.percentile(AY_corr[s,j+k],75) - np.percentile(AY_corr[s,j+k],25))*len(AY_corr[s,j+k])**(-1/3)
- corr_range = np.max(AY_corr[s,j+k]) - np.min(AY_corr[s,j+k])
- num_bins = int(corr_range/(fd/2))
- counts, bins = np.histogram(AY_corr[s,j+k], bins=num_bins)
- hist_plot = sns.histplot(data=AY_corr[s,j+k], bins=num_bins, kde=True, color=color_list[i][k], edgecolor=color_list[i][k], label=title_list[j+k],ax=ax)
- pct = np.percentile(AY_corr[s,j+k], [15, 85])
- pct_neg.append(pct[0])
- pct_pos.append(pct[1])
- for q, p in enumerate(pct):
- ind = np.argmin(np.abs(bins - p))
- ax.vlines(p, 0, counts[ind], colors=color_list[i][k], linestyles='dashed', linewidth=2)
- if q == 0:
- h_pct_neg.append(counts[ind])
- else:
- h_pct_pos.append(counts[ind])
- if k == 0:
- thr_corr = pvalue_to_r(0.01/44318, n_list[j+k-1])
- ind = np.argmin(np.abs(bins - thr_corr))
- ax.vlines(thr_corr, 0, counts[ind], colors='k', linestyles='dotted', linewidth=2)
- ax.text(thr_corr-0.2, counts[ind]+500, "$p=\\frac{0.01}{44318}$", fontsize=16)
- ax.annotate("", xy=(thr_corr, counts[ind]), xytext=(thr_corr, counts[ind]+400), arrowprops=dict(arrowstyle="->"))
- ind = np.argmin(np.abs(bins + thr_corr))
- ax.vlines(-thr_corr, 0, counts[ind], colors='k', linestyles='dotted', linewidth=2)
- ax.text(-thr_corr-0.62, counts[ind]+500, "$p=\\frac{0.01}{44318}$", fontsize=16)
- ax.annotate("", xy=(-thr_corr, counts[ind]), xytext=(-thr_corr, counts[ind]+400), arrowprops=dict(arrowstyle="->"))
- ax.text(min(pct_neg)-0.32, 50, "15%", fontsize=13)
- ax.text(max(pct_pos)-0.04, 50, "15%", fontsize=13)
- # ax.set_title(f"Subspace {s+1}", fontsize=16)
- ax.set_xlabel("Cross-modal correlation", fontsize=15)
- ax.set_ylabel("Count", fontsize=15)
- ax.set_xlim([-1, 1])
- ax.set_ylim([0, 2500])
- ax.legend(loc="upper left", fontsize=13) # bbox_to_anchor=(0.92, 1.35)
- plt.tick_params(axis='both', labelsize=12)
- plt.savefig(f"figures/AY_histogram/sz/subspace{s+1}_{group_list[i]}.png", bbox_inches="tight", dpi=500)
- # %%
- Xpath = os.path.join(res_dir, "mat", "sMRI-fMRI", "X_sz.mat")
- X = sio.loadmat(Xpath)['X']
- X = np.squeeze(X)
- X[0].shape
- # %%
- AY_corr = np.zeros((5, 44318))
- for m in range(2):
- Xm = X[m][:,id]
- Xm_demean = Xm - np.mean(Xm)
- sstot = np.sum(Xm_demean**2)
- for s, i in enumerate(np.arange(0,10,2)):
- Am = A[1,1,m,:,i:i+2]
- Ym = Y[1,1,m,i:i+2]
- AYm = Am@Ym
- ssres = np.sum((Xm-AYm)**2)
- r2 = (1 - ssres/sstot)*100
- print(f"modality {m+1} subspace {s+1} variance explained: {r2:.3f}%")
plot_img_sz.ipynb at commit 4126a6f, under Apache-2.0 · at the source
Overview
- Tri-institutional Center for Translational Research in Neuroimaging and Data Science, Georgia State University, Georgia Institute of Technology, Emory University, Atlanta, GA, United States
- School of Electrical and Computer Engineering, Georgia Institute of Technology, Atlanta, GA, United States
- Department of Psychiatry and Behavioral Sciences, McGovern Medical School, University of Texas Health Science Center at Houston, Houston, TX, United States
- Department of Computer Science and Electrical Engineering, University of Maryland Baltimore County, Baltimore, MD, United States
Abstract
A key challenge in neuroscience is inferring relationships between brain structure and function from high-dimensional, multimodal neuroimaging data. While conventional multivariate approaches often simplify statistical assumptions and estimate one-dimensional independent sources shared across modalities, the true relationships between latent sources are likely more complex—statistical dependence may exist both within and between modalities and span more than one dimension per modality. Here, we introduce Multimodal Subspace Independent Vector Analysis (MSIVA), a method for capturing both joint and unique vector sources from multiple data modalities by defining cross-modal and unimodal subspaces with variable dimensions. MSIVA enables flexible estimation of varying-size independent subspaces within modalities and their one-to-one linkage to corresponding subspaces across modalities. Crucially, it captures subject-level variability at the voxel level within independent subspaces, in contrast to traditional methods that share identical independent components across subjects. We evaluated three initialization workflows with five candidate subspace structures in multiple synthetic datasets and two large multimodal neuroimaging datasets, including structural MRI (sMRI) and functional MRI (fMRI). After confirming that MSIVA successfully recovered ground-truth subspace structures in synthetic data, we applied MSIVA to identify latent subspace structures in neuroimaging data. Subsequent subspace-specific canonical correlation analysis, brain-phenotype prediction, and voxelwise brain-age delta analysis revealed that MSIVA sources were strongly associated with multiple phenotype variables, including age, sex, schizophrenia, lifestyle factors, and cognitive functions. Further, we identified modality- and group-specific brain regions related to age (for example, cerebellum, precentral gyrus, and cingulate gyrus in sMRI; occipital lobe and superior frontal gyrus in fMRI), sex (for example, cerebellum in sMRI, frontal lobe in fMRI, and precuneus in both sMRI and fMRI), and schizophrenia (for example, cerebellar, frontal, and insular cortices in sMRI; occipital pole, lingual gyrus, and precuneus in fMRI), shedding light on linked phenotypic and neuropsychiatric biomarkers of brain structure and function.
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above, with 21 matches between paragraphs and lines of code.
trendscenter/MSIVA
4126a6f9db37b123b703c463cb017bd5f3288820, 20 August 2026Availability: 1 check, the latest on 27 September 2026: the link answers
- 27 September 2026: the link answers
146 files
- @MISAK/
IVAfy.m , MATLAB, 15 lines - @MISAK/
MISAK.m , MATLAB, 122 lines, 1 match - @MISAK/
MISI.m , MATLAB, 12 lines - @MISAK/
MMD.m , MATLAB, 5 lines - @MISAK/
MMSE.m , MATLAB, 5 lines - @MISAK/
auto_tune.m , MATLAB, 69 lines - @MISAK/
combinatorial_optim.m , MATLAB, 24 lines - @MISAK/
greedy_sub_perm_analysis , MATLAB, 98 lines.m - @MISAK/
greedysearch.m , MATLAB, 219 lines - @MISAK/
greedysearch_flex.m , MATLAB, 237 lines - @MISAK/
greedysearch_iva.m , MATLAB, 223 lines - @MISAK/
objective.m , MATLAB, 23 lines - @MISAK/
objective_.m , MATLAB, 227 lines - @MISAK/
objective_sc_.m , MATLAB, 206 lines - @MISAK/
stackW.m , MATLAB, 7 lines - @MISAK/
sub_perm_analysis.m , MATLAB, 122 lines - @MISAK/
unstackW.m , MATLAB, 14 lines - @MISAK/
update.m , MATLAB, 33 lines - @MISAK/
updateCS.m , MATLAB, 59 lines - @MISAK/
updategradtype.m , MATLAB, 17 lines - @MISAK/
updatesc.m , MATLAB, 9 lines - @MISAKRE/
MISAKRE.m , MATLAB, 49 lines - @MISAKRE/
RE_.m , MATLAB, 143 lines - @MISAKRE/
con_RE.m , MATLAB, 123 lines - @MISAKRE/
opt_RE.m , MATLAB, 118 lines - @MISAKRE/
orthY.m , MATLAB, 10 lines - @MISAKRE/
reg_RE.m , MATLAB, 18 lines - @MISAKRE/
setREapproach.m , MATLAB, 17 lines - @MISAKRE/
setREref.m , MATLAB, 140 lines - @MISAKRE/
setREtype.m , MATLAB, 14 lines - @MISAKRE/
updateRElambda.m , MATLAB, 5 lines - @MISAKRE/
updateREreflambda.m , MATLAB, 5 lines - @gsd/
genX.m , MATLAB, 5 lines - @gsd/
gsd.m , MATLAB, 364 lines - @gsd/
saveX.m , MATLAB, 6 lines - @gsd/
saveme.m , MATLAB, 5 lines - @gsd/
select_from_set.m , MATLAB, 9 lines - @gsm/
genX.m , MATLAB, 5 lines - @gsm/
gsm.m , MATLAB, 333 lines - @gsm/
saveX.m , MATLAB, 6 lines - @gsm/
saveme.m , MATLAB, 5 lines - @utils/
MISI.m , MATLAB, 43 lines - @utils/
MMD.m , MATLAB, 38 lines - @utils/
MMSE.m , MATLAB, 46 lines - @utils/
doMMGPCA.m , MATLAB, 55 lines - @utils/
getop.m , MATLAB, 46 lines - @utils/
munkres.m , MATLAB, 200 lines - @utils/
myISI.m , MATLAB, 15 lines - @utils/
myMD.m , MATLAB, 19 lines - @utils/
myMSE.m , MATLAB, 31 lines - @utils/
myPCA.m , MATLAB, 26 lines - @utils/
myicdf.m , MATLAB, 125 lines - @utils/
mymvk.m , MATLAB, 55 lines, 1 match - @utils/
mymvlap.m , MATLAB, 45 lines, 1 match - @utils/
run_MISA.m , MATLAB, 18 lines - @utils/
stackMuCov.m , MATLAB, 8 lines - @utils/
stackW.m , MATLAB, 6 lines - @utils/
unstackMuCov.m , MATLAB, 21 lines - @utils/
unstackW.m , MATLAB, 13 lines - @utils/
utils.m , MATLAB, 35 lines - figures/
IMAG2026/ , MATLAB, 138 linesage_delta.m - figures/
IMAG2026/ , Python, 182 linesancillary.py - figures/
IMAG2026/ , Jupyter, 178 lines, 2 matchescompare_mmiva_msiva_sz.i pynb - figures/
IMAG2026/ , Jupyter, 170 linescompare_mmiva_msiva_ukb. ipynb - figures/
IMAG2026/ , Python, 16 linescompute_geometric_median .py - figures/
IMAG2026/ , MATLAB, 255 linesdualcodeImage_AY_geomedi an.m - figures/
IMAG2026/ , MATLAB, 177 linesdualcodeImage_beta1.m - figures/
IMAG2026/ , MATLAB, 183 linesdualcodeImage_delta2p_ge omedian.m - figures/
IMAG2026/ , MATLAB, 182 linesdualcodeImage_delta2p_st d.m - figures/
IMAG2026/ , MATLAB, 198 linesdualcodeImage_phenotype. m - figures/
IMAG2026/ , Python, 54 linesphenotype_map.py - figures/
IMAG2026/ , Jupyter, 51 linesplot_img_loss.ipynb - figures/
IMAG2026/ , Jupyter, 622 lines, 6 matchesplot_img_sz.ipynb - figures/
IMAG2026/ , Jupyter, 180 lines, 1 matchplot_img_sz_rdc.ipynb - figures/
IMAG2026/ , Jupyter, 564 lines, 2 matchesplot_img_ukb.ipynb - figures/
IMAG2026/ , Jupyter, 180 lines, 1 matchplot_img_ukb_rdc.ipynb - figures/
IMAG2026/ , Jupyter, 59 linesplot_init_corr.ipynb - figures/
IMAG2026/ , Jupyter, 53 linesplot_itc.ipynb - figures/
IMAG2026/ , Jupyter, 354 lines, 2 matchesplot_loss.ipynb - figures/
IMAG2026/ , Jupyter, 93 linesplot_num_crossmodal_voxe l.ipynb - figures/
IMAG2026/ , Jupyter, 78 lines, 2 matchesplot_sig_voxel.ipynb - figures/
IMAG2026/ , Jupyter, 359 linesplot_sim.ipynb - figures/
IMAG2026/ , Jupyter, 92 linesplot_subspace_struct.ipy nb - figures/
IMAG2026/ , Python, 540 lines, 1 matchutils.py - figures/
ISBI2023/ , Python, 182 linesancillary.py - figures/
ISBI2023/ , MATLAB, 32 linesconvert_to_RGB.m - figures/
ISBI2023/ , MATLAB, 165 linesdualmap.m - figures/
ISBI2023/ , Jupyter, 300 linesplot_img.ipynb - figures/
ISBI2023/ , Jupyter, 152 linesplot_sim.ipynb - figures/
ISBI2023/ , Jupyter, 92 linesplot_subspace_struct.ipy nb - other_methods/
icatb_iva_laplace.m , MATLAB, 947 lines - other_methods/
icatb_iva_laplace_bkt.m , MATLAB, 996 lines - other_methods/
icatb_iva_second_order.m , MATLAB, 1,526 lines - other_methods/
icatb_runica.m , MATLAB, 980 lines - other_methods/
isa_est.m , MATLAB, 145 lines - other_methods/
jbd.m , MATLAB, 339 lines, 1 match - scripts/
ICA1/ , MATLAB, 289 linessimICA_PhD.m - scripts/
ISA1_2/ , MATLAB, 421 linesMISA_ISA_run.m - scripts/
ISA3/ , MATLAB, 382 linessimISA_PhD.m - scripts/
IVA1/ , MATLAB, 251 linesMISA_IVA_run.m - scripts/
IVA2/ , MATLAB, 628 linessimspatialIVA_PhD.m - scripts/
MCIv4/ , MATLAB, 42 linesmci_create_4DNiftifile.m - scripts/
MCIv4/ , MATLAB, 242 linesmci_finputcheck.m - scripts/
MCIv4/ , MATLAB, 20 linesmci_getclusters.m - scripts/
MCIv4/ , MATLAB, 45 linesmci_interp2struct.m - scripts/
MCIv4/ , MATLAB, 54 linesmci_interpdata.m - scripts/
MCIv4/ , MATLAB, 94 linesmci_makeimage.m - scripts/
MCIv4/ , MATLAB, 104 linesmci_makesubplotgrid_clus ter.m - scripts/
MCIv4/ , MATLAB, 34 linesmci_plotch.m - scripts/
MCIv4/ , MATLAB, 27 linesmci_plotcolorbar.m - scripts/
MCIv4/ , MATLAB, 230 linesmci_plotcomps.m - scripts/
MCIv4/ , MATLAB, 8 linesmci_plotlabels.m - scripts/
MCIv4/ , MATLAB, 19 linesmci_plotslicepos.m - scripts/
MCIv4/ , MATLAB, 23 linesmci_plotslices.m - scripts/
MCIv4/ , MATLAB, 45 linesmci_sample.m - scripts/
MCIv4/ , MATLAB, 8 linesmci_save_as_nii.m - scripts/
MCIv4/ , MATLAB, 5 linesmci_simplify_coords.m - scripts/
SIVA/ , MATLAB, 161 linesfunc_img.m - scripts/
SIVA/ , MATLAB, 237 linesfunc_sim.m - scripts/
SIVA/ , MATLAB, 158 linesrun_mgpca_gica.m - scripts/
SIVA/ , MATLAB, 164 linesrun_mgpca_ica.m - scripts/
SIVA/ , MATLAB, 168 linesrun_pca_ica.m - scripts/
doSecondPCAStep.m , MATLAB, 11 lines - scripts/
my_3views.m , MATLAB, 24 lines - scripts/
setup_basic_MISAKRE.m , MATLAB, 23 lines - scripts/
setup_hybrid_MISAKRE.m , MATLAB, 23 lines - scripts/
sim_MISA.m , MATLAB, 49 lines - scripts/
sim_basic_ICA.m , MATLAB, 24 lines - scripts/
sim_basic_ISA.m , MATLAB, 28 lines - scripts/
sim_basic_IVA.m , MATLAB, 30 lines - scripts/
sim_basic_SIVA.m , MATLAB, 49 lines - scripts/
sim_hybrid_IVA.m , MATLAB, 21 lines - scripts/
simhybridICAfMRI.m , MATLAB, 338 lines - scripts/
simhybridICAfMRI_maps_vi , MATLAB, 220 linesew.m - scripts/
simhybridMISA.m , MATLAB, 298 lines - scripts/
simhybridMISA_maps_view. , MATLAB, 195 linesm - scripts/
simhybridMMIVA.m , MATLAB, 330 lines - scripts/
simhybridMMIVA_maps_view , MATLAB, 404 lines.m - scripts/
to_vol.m , MATLAB, 17 lines - scripts/
toy_example/ , MATLAB, 80 linesexecute_full_optimizatio n.m - scripts/
toy_example/ , MATLAB, 51 linesget_MISA_parameters.m - scripts/
toy_example/ , MATLAB, 84 linessetup_and_run.m - scripts/
toy_example/ , MATLAB, 35 linesview_results.m - validateFirstDerivatives
_.m , MATLAB, 277 lines - LICENSE, License, 201 lines
- README.md, Text, 113 lines
The paper's code and data availability statement is in the Data section.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 144 scripts, each with its path and the digest of its content;
- 21 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data and Code Availability
The UK Biobank dataset can be accessed at https://
All code used in this study is publicly available at https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 2, 28 September 2026
- Funding: added National Science Foundation: 2316420, R01MH123610, 2112455; Emory University; National Institutes of Health: 5r01mh123610-04, T32EB025816, R01 MH123610; National Institute of Biomedical Imaging and Bioengineering: T32 EB025816
Version 1, 27 September 2026: the first record
Recorded: type, language, journal, volume, pages, dates, 5 authors, 7 keywords, 79 references.
Cite
This paper
Li, X., Kochunov, P., Adali, T., Silva, R. F., & Calhoun, V. D. (2026). Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function. Imaging neuroscience (Cambridge, Mass.), 4, IMAG.a.1266. https://
BibTeX
@article{li2026multimoda
author = {Li, Xinhui and Kochunov, Peter and Adali, Tulay and Silva, Rogers F and Calhoun, Vince D},
title = {{Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function}},
journal = {Imaging neuroscience (Cambridge, Mass.)},
year = {2026},
month = jun,
volume = {4},
pages = {IMAG.a.1266},
publisher = {MIT Press},
issn = {2837-6056},
doi = {10.1162/
url = {https://
pmid = {42326564},
pmcid = {PMC13281777}
}
RIS
TY - JOUR
AU - Li, Xinhui
AU - Kochunov, Peter
AU - Adali, Tulay
AU - Silva, Rogers F
AU - Calhoun, Vince D
TI - Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function
T2 - Imaging neuroscience (Cambridge, Mass.)
J2 - Imaging Neurosci (Camb)
PY - 2026
DA - 2026/
VL - 4
SP - IMAG.a.1266
SN - 2837-6056
PB - MIT Press
DO - 10.1162/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1162/
"type": "article-journal",
"title": "Multimodal subspace independent vector analysis effectively captures latent relationships between brain structure and function",
"container-title": "Imaging neuroscience (Cambridge, Mass.)",
"author": [
{
"family": "Li",
"given": "Xinhui"
},
{
"family": "Kochunov",
"given": "Peter"
},
{
"family": "Adali",
"given": "Tulay"
},
{
"family": "Silva",
"given": "Rogers F"
},
{
"family": "Calhoun",
"given": "Vince D"
}
],
"container-title-short":
"volume": "4",
"page": "IMAG.a.1266",
"DOI": "10.1162/
"PMID": "42326564",
"PMCID": "PMC13281777",
"ISSN": "2837-6056",
"publisher": "MIT Press",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
6,
18
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1002/hbm.70599 [code]
- Group Joint ICA (gjICA): A Method for Multimodal Fusion of Concurrent EEG and fMRI Data.Journal: Human brain mappingIn common: GIFT, export_fig, Optimization Toolbox, 4 other tools, fMRI, 6 references, author Vince Calhoun
- [2] doi:10.1162/netn.a.575 [code]
- Parallel multilink group joint ICA (pmg-jICA): Fusion of 3D structural and 4D functional data across multiple resting fMRI networks.Journal: Network neuroscience (Cambridge, Mass.)In common: GIFT, SPM, Statistics and Machine Learning Toolbox, 3 other tools, fMRI, 5 references, author Tülay Adali
- [3] doi:10.1162/imag.a.1220 [code]
- Brain functional network connectivity interpolation characterizes the neuropsychiatric continuum and heterogeneity.Journal: Imaging neuroscience (Cambridge, Mass.)In common: seaborn, scikit-learn, pandas, 3 other tools, schizophrenia / psychosis, 2 references, 2 authors
- [4] doi:10.1038/s41467-026-71151-2 [code]
- Common and distinct neural correlates of social interaction processing and theory of mind in narratives.Journal: Nature communicationsIn common: GIFT, Optimization Toolbox, Nilearn, 11 other tools
- [5] doi:10.1038/s41586-026-10631-3 [code]
- A prognostic human brain network for diffuse midline glioma.Journal: NatureIn common: export_fig, Optimization Toolbox, Nilearn, 10 other tools, clinical / translational
- [6] doi:10.1371/journal.pbio.3003856 [code]
- Aging and metabolism contribute separately to brain-body health.Journal: PLoS biologyIn common: Nilearn, SPM, Image Processing Toolbox, 9 other tools, clinical / translational, 2 references
- [7] doi:10.1038/s41398-026-04278-x [code]
- Aberrant recovery of timescale-aligned amplitude balance links to symptoms and cognition in schizophrenia.Journal: Translational psychiatryIn common: GIFT, Statistics and Machine Learning Toolbox, schizophrenia / psychosis, 3 references, author Vince Calhoun
- [8] doi:10.1038/s42003-026-10276-y [code]
- The cellular correlates and adolescent reorganisation of cortical myelination networks in the common marmoset.Journal: Communications biologyIn common: Optimization Toolbox, SPM, Image Processing Toolbox, 9 other tools, 1 reference
- [9] doi:10.64898/2026.08.13.26360304 [code]
- Lifespan brain structural variation reveals shared organization across mental health conditionsJournal: medRxiv (preprint)In common: Nilearn, NiBabel, statsmodels, 6 other tools, clinical / translational, author Vince Calhoun
- [10] doi:10.1002/hbm.70602 [code]
- Neuroimaging Correlates of Post-Stroke Pain After Ischemic Stroke: Secondary Analysis of the INSPiRE-TMS Trial.Journal: Human brain mappingIn common: export_fig, Optimization Toolbox, SPM, 8 other tools, clinical / translational
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 144 scripts, and 21 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:9064f4c4468ff7ba…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
