├── README.md ├── data └── hcp_group_mtrx-400.mat ├── example_script.m └── fcn ├── communicability_wei.m ├── distance_bin.m ├── distance_wei_floyd.m ├── fcn_flow_graph.m ├── get_components.m ├── matching_ind_und.m ├── mean_first_passage_time.m ├── navigate.m ├── path_transitivity.m ├── retrieve_shortest_path.m └── search_information.m /README.md: -------------------------------------------------------------------------------- 1 | # local_scfc 2 | Calculate structure-function coupling at a local level as in Esfahlani et al (2021). 3 | 4 | # dependencies and requirements 5 | This script uses MATLAB. It was tested using MATLAB 2020a on macOS Mojave 10.14.6. It requires no additional software packages. 6 | 7 | # installation and usage 8 | Download and unzip the directory, open MATLAB, and then navigate to wherever you downloaded the files. You can run the script from within that directory. 9 | 10 | # what is here 11 | * Group-representative sc/fc/euclidean distance in 400-node parcellation. 12 | * Directory containing functions for generating predictors based on sc matrix. 13 | * Example script to implement the procedure. 14 | 15 | # what does the script do? 16 | 1. Reads in sc/fc/euclidean distance data 17 | 2. Generates a set of predictors 18 | 3. Calculates local (regional) correlations of predictors with FC. 19 | 20 | # if you want to apply this method to your data 21 | * Replace variables sc, fc, d with your own structural connectivity, functional connectivity, and inter-regional Euclidean distance matrix. 22 | 23 | Runtime for entire script is < 10 s. 24 | 25 | If you use this code, please cite: 26 | Esfahlani, F. Z., Faskowitz, J., Slack, J., Misic, B., & Betzel, R. (2021). Local structure-function relationships in human brain networks across the human lifespan. bioRxiv. [(link to paper)](https://www.biorxiv.org/content/10.1101/2021.05.23.445128v1.abstract) 27 | -------------------------------------------------------------------------------- /data/hcp_group_mtrx-400.mat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brain-networks/local_scfc/9f418715b2178706c9e3193bf71a56a9854560ec/data/hcp_group_mtrx-400.mat -------------------------------------------------------------------------------- /example_script.m: -------------------------------------------------------------------------------- 1 | clear all 2 | close all 3 | clc 4 | 5 | % functions come from: 6 | % • Brain connectivity toolbox (https://sites.google.com/site/bctnet/) 7 | % • Caio Seguin's github (https://github.com/caioseguin/connectomics/) 8 | % • Custom code written for this paper specifically 9 | 10 | % if you use this script, please cite: 11 | % 12 | % Esfahlani, F. Z., Faskowitz, J., Slack, J., Misic, B., & Betzel, R. 13 | % (2021). Local structure-function relationships in human brain networks 14 | % across the human lifespan. bioRxiv. 15 | % (https://doi.org/10.1101/2021.05.23.445128). 16 | 17 | %% set up path structure and load data 18 | 19 | % add functions to path 20 | addpath(genpath('fcn')); 21 | 22 | % load group-representative sc/fc/euclidean distance data 23 | load data/hcp_group_mtrx-400.mat 24 | 25 | % define weighted and binary versions of sc 26 | a = sc; 27 | abin = +(sc > 0); 28 | n = length(sc); 29 | 30 | %% generate predictor matrices 31 | % this section generates a series of fully-weighted and signed matrices to 32 | % be used as predictors for fc. 33 | 34 | % for path-based measures, we transform connection weights to costs via the 35 | % following equation: cost = weight.^-gamma. the user needs to specify the 36 | % range of gamma values. similarly, for flow graphs the user needs to 37 | % specify markov times at which to evaluate the matrix. 38 | 39 | % gamma values 40 | gammavals = [0.25,0.5,1,2]; 41 | 42 | % markov times 43 | tvals = [1,2.5,5,10]; 44 | 45 | %% binary predictors 46 | PLbin = distance_bin(abin); % path length 47 | Gbin = expm(abin); % communicability 48 | Cosbin = 1 - squareform(pdist(abin,'cosine')); % cosine distance 49 | SIbin = search_information(abin,'inv',false); % search info 50 | PTbin = path_transitivity(abin,'inv'); % path transitivity 51 | mfptbin = zscore(mean_first_passage_time(abin));% mean first passage time 52 | MIbin = matching_ind_und(abin); % matching index 53 | 54 | FGbin = zeros(n,n,length(tvals)); % flow graphs 55 | for itime = 1:length(tvals) 56 | FGbin(:,:,itime) = fcn_flow_graph(abin,ones(n,1),tvals(itime)); 57 | end 58 | 59 | %% weighted predictors 60 | Gwei = communicability_wei(a); % communicability 61 | Coswei = 1 - squareform(pdist(a,'cosine')); % cosine distance 62 | mfptwei = zscore(mean_first_passage_time(a)); % mean first passage time 63 | MIwei = matching_ind_und(a); % matching index 64 | PLwei = zeros(n,n,length(gammavals)); % path length 65 | SIwei = PLwei; % search info 66 | PTwei = PLwei; % path transitivity 67 | for igamma = 1:length(gammavals) 68 | 69 | L = a.^(-gammavals(igamma)); % convert weight to cost 70 | PLwei(:,:,igamma) = distance_wei_floyd(L); 71 | 72 | L(isinf(L)) = 0; 73 | SIwei(:,:,igamma) = search_information(L,[],false); 74 | PTwei(:,:,igamma) = path_transitivity(L,[]); 75 | 76 | end 77 | FGwei = FGbin; % flow graphs 78 | for itime = 1:length(tvals) 79 | FGwei(:,:,itime) = fcn_flow_graph(a,ones(n,1),tvals(itime)); 80 | 81 | end 82 | 83 | %% navigation-based predictors 84 | nav_struct = navigate(a,d); 85 | failed = nav_struct.failed_paths; 86 | nav_struct.num_hops(failed == 1) = inf; 87 | nav_struct.pl_MS(failed == 1) = inf; 88 | 89 | Navnumhops = nav_struct.num_hops; % number of hops 90 | NavplMS = nav_struct.pl_MS; % total distance 91 | 92 | %% aggregate predictors and construct local multi-linear models 93 | % concatenate all predictors into single array 94 | mats = cat(3,d,PLbin,PLwei,Gwei,Gbin,Coswei,Cosbin,SIbin,SIwei,PTbin,PTwei,MIwei,MIbin,Navnumhops,NavplMS,mfptwei,mfptbin,FGbin,FGwei); 95 | 96 | % preallocate array for storing correlation coefficients 97 | r = zeros(n,size(mats,3)); 98 | for p = 1:size(mats,3) 99 | for i = 1:n 100 | 101 | % column of fc (from one region) 102 | y = fc(:,i); 103 | 104 | % column from predictor p 105 | x = mats(:,i,p); 106 | 107 | % remove node i 108 | y(i) = []; 109 | x(i,:) = []; 110 | 111 | % ignore nans/infs 112 | mask = ~isnan(x) & ~isinf(x) & ~isnan(y) & ~isinf(y); 113 | x = x(mask); 114 | y = y(mask); 115 | 116 | % z-score predictor 117 | x = x - nanmean(x); 118 | x = x/nanstd(x); 119 | 120 | % z-score fc 121 | y = y - nanmean(y); 122 | y = y/nanstd(y); 123 | 124 | % calculate correlation 125 | r(i,p) = corr(x,y); 126 | 127 | % note: could replace call to 'corr' with 'regress' 128 | %[beta,~,~,~,stats] = regress(y,[ones(length(y),1),x]); 129 | %s(i,p) = sign(beta(2))*sqrt(stats(1)); 130 | 131 | end 132 | end 133 | 134 | % transform correlation coefficients into variance explained 135 | rsq = r.^2; -------------------------------------------------------------------------------- /fcn/communicability_wei.m: -------------------------------------------------------------------------------- 1 | function F = communicability_wei(CIJ) 2 | 3 | %inputs 4 | % CIJ weighted connection matrix 5 | % i row 6 | % j column 7 | % 8 | %outputs 9 | % F communicability 10 | %================================================= 11 | 12 | N = size(CIJ,1); 13 | B = sum(CIJ')'; 14 | C = diag(B); 15 | D = C^(-(1/2)); 16 | E = D * CIJ * D; 17 | F = expm(E); 18 | F = F.*~eye(N); -------------------------------------------------------------------------------- /fcn/distance_bin.m: -------------------------------------------------------------------------------- 1 | function D=distance_bin(A) 2 | %DISTANCE_BIN Distance matrix 3 | % 4 | % D = distance_bin(A); 5 | % 6 | % The distance matrix contains lengths of shortest paths between all 7 | % pairs of nodes. An entry (u,v) represents the length of shortest path 8 | % from node u to node v. The average shortest path length is the 9 | % characteristic path length of the network. 10 | % 11 | % Input: A, binary directed/undirected connection matrix 12 | % 13 | % Output: D, distance matrix 14 | % 15 | % Notes: 16 | % Lengths between disconnected nodes are set to Inf. 17 | % Lengths on the main diagonal are set to 0. 18 | % 19 | % Algorithm: Algebraic shortest paths. 20 | % 21 | % 22 | % Mika Rubinov, U Cambridge 23 | % Jonathan Clayden, UCL 24 | % 2007-2013 25 | 26 | % Modification history: 27 | % 2007: Original (MR) 28 | % 2013: Bug fix, enforce zero distance for self-connections (JC) 29 | 30 | A=double(A~=0); %binarize and convert to double format 31 | 32 | l=1; %path length 33 | Lpath=A; %matrix of paths l 34 | D=A; %distance matrix 35 | 36 | Idx=true; 37 | while any(Idx(:)) 38 | l=l+1; 39 | Lpath=Lpath*A; 40 | Idx=(Lpath~=0)&(D==0); 41 | D(Idx)=l; 42 | end 43 | 44 | D(~D)=inf; %assign inf to disconnected nodes 45 | D(1:length(A)+1:end)=0; %clear diagonal -------------------------------------------------------------------------------- /fcn/distance_wei_floyd.m: -------------------------------------------------------------------------------- 1 | function [SPL,hops,Pmat] = distance_wei_floyd(D,transform) 2 | % DISTANCE_WEI_FLOYD Distance matrix (Floyd-Warshall algorithm) 3 | % 4 | % [SPL,hops,Pmat] = distance_wei_floyd(D,transform) 5 | % 6 | % Computes the topological length of the shortest possible path 7 | % connecting every pair of nodes in the network. 8 | % 9 | % Inputs: 10 | % 11 | % D, 12 | % Weighted/unweighted directed/undirected 13 | % connection *weight* OR *length* matrix. 14 | % 15 | % transform, 16 | % If the input matrix is a connection *weight* matrix, specify a 17 | % transform that map input connection weights to connection 18 | % lengths. Two transforms are available. 19 | % 'log' -> l_ij = -log(w_ij) 20 | % 'inv' -> l_ij = 1/w_ij 21 | % 22 | % If the input matrix is a connection *length* matrix, do not 23 | % specify a transform (or specify an empty transform argument). 24 | % 25 | % 26 | % Outputs: 27 | % 28 | % SPL, 29 | % Unweighted/Weighted shortest path-length matrix. 30 | % If W is directed matrix, then SPL is not symmetric. 31 | % 32 | % hops, 33 | % Number of edges in the shortest path matrix. If W is 34 | % unweighted, SPL and hops are identical. 35 | % 36 | % Pmat, 37 | % Elements {i,j} of this matrix indicate the next node in the 38 | % shortest path between i and j. This matrix is used as an input 39 | % argument for function 'retrieve_shortest_path.m', which returns 40 | % as output the sequence of nodes comprising the shortest path 41 | % between a given pair of nodes. 42 | % 43 | % 44 | % Notes: 45 | % 46 | % There may be more than one shortest path between any pair of nodes 47 | % in the network. Non-unique shortest paths are termed shortest path 48 | % degeneracies, and are most likely to occur in unweighted networks. 49 | % When the shortest-path is degenerate, The elements of matrix Pmat 50 | % correspond to the first shortest path discovered by the algorithm. 51 | % 52 | % The input matrix may be either a connection weight matrix, or a 53 | % connection length matrix. The connection length matrix is typically 54 | % obtained with a mapping from weight to length, such that higher 55 | % weights are mapped to shorter lengths (see above). 56 | % 57 | % 58 | % Algorithm: Floyd–Warshall Algorithm 59 | % 60 | % 61 | % Andrea Avena-Koenigsberger, IU, 2012 62 | 63 | % Modification history 64 | % 2016 - included transform variable that maps weights to lengths 65 | 66 | if exist('transform','var') && ~isempty(transform) 67 | 68 | switch transform 69 | 70 | case 'log' 71 | 72 | if any((D<0) & D>1) 73 | error('connection-strengths must be in the interval [0,1) to use the transform -log(w_ij) \n') 74 | else 75 | SPL = -log(D); 76 | end 77 | 78 | case 'inv' 79 | 80 | SPL = 1./D; 81 | 82 | otherwise 83 | 84 | error('Unexpected transform type. Only "log" and "inv" are accepted \n') 85 | end 86 | 87 | else % the input is a connection lengths matrix. 88 | SPL = D; 89 | SPL(SPL == 0) = inf; 90 | end 91 | 92 | n=size(D,2); 93 | 94 | if nargout > 1 95 | flag_find_paths = true; 96 | hops = double(D ~= 0); 97 | Pmat = 1:n; 98 | Pmat = Pmat(ones(n,1),:); 99 | else 100 | flag_find_paths = false; 101 | end 102 | 103 | for k=1:n 104 | i2k_k2j = bsxfun(@plus, SPL(:,k), SPL(k,:)); 105 | 106 | if flag_find_paths 107 | path = bsxfun(@gt, SPL, i2k_k2j); 108 | [i,j] = find(path); 109 | hops(path) = hops(i,k) + hops(k,j)'; 110 | Pmat(path) = Pmat(i,k); 111 | end 112 | 113 | SPL = min(SPL, i2k_k2j); 114 | end 115 | 116 | SPL(eye(n)>0)=0; 117 | 118 | if flag_find_paths 119 | hops(eye(n)>0)=0; 120 | Pmat(eye(n)>0)=0; 121 | end 122 | -------------------------------------------------------------------------------- /fcn/fcn_flow_graph.m: -------------------------------------------------------------------------------- 1 | function dyn = fcn_flow_graph(aij,r,t) 2 | %FCN_flow_graph continuous time random walk on network 3 | % 4 | % DYN = FCN_flow_graph(AIJ,R,T) instantiates a continuous time 5 | % random walk on network with adjacency matrix AIJ. Waiting time 6 | % for walkers at each node are distributed as Poisson with rate 7 | % parameter R. This function returns the flow graph at time T. 8 | % 9 | % Inputs: AIJ, symmetric adjacency matrix 10 | % R, rate parameter 11 | % T, markov time 12 | % 13 | % Outputs: DYN, flow graph at time T 14 | % 15 | % Reference: Lambiotte et al 2011. DOI: 10.1103/PhysRevE.84.017102 16 | % 17 | % Richard Betzel, Indiana University, 2012 18 | % 19 | 20 | %modification history 21 | %04.18.2012 - original 22 | %07.20.2012 - added symmetrizing step (dyn + dyn')/2 to correct for 23 | % precision issues 24 | 25 | [rr,cr] = size(r); 26 | if rr > cr 27 | r = r'; 28 | end 29 | s = sum(aij); 30 | z = sum(s./r); 31 | ps = s./(z.*r); 32 | lap = -(bsxfun(@times,bsxfun(@rdivide,aij,s),r) - diag(r)); 33 | 34 | dyn = bsxfun(@times,z*expm(-t*lap),ps); 35 | dyn = (dyn + dyn')./2; -------------------------------------------------------------------------------- /fcn/get_components.m: -------------------------------------------------------------------------------- 1 | function [comps,comp_sizes] = get_components(adj) 2 | %GET_COMPONENTS connected components 3 | % 4 | % [comps,comp_sizes] = get_components(adj); 5 | % 6 | % Returns the components of an undirected graph specified by the binary and 7 | % undirected adjacency matrix adj. Components and their constitutent nodes are 8 | % assigned the same index and stored in the vector, comps. The vector, comp_sizes, 9 | % contains the number of nodes beloning to each component. 10 | % 11 | % Inputs: adj, binary and undirected adjacency matrix 12 | % 13 | % Outputs: comps, vector of component assignments for each node 14 | % comp_sizes, vector of component sizes 15 | % 16 | % Note: disconnected nodes will appear as components with a component 17 | % size of 1 18 | % 19 | % J Goni, University of Navarra and Indiana University, 2009/2011 20 | 21 | 22 | if size(adj,1)~=size(adj,2) 23 | error('this adjacency matrix is not square'); 24 | end 25 | 26 | if ~any(adj-triu(adj)) 27 | adj = adj | adj'; 28 | end 29 | 30 | %if main diagonal of adj do not contain all ones, i.e. autoloops 31 | if sum(diag(adj))~=size(adj,1) 32 | 33 | %the main diagonal is set to ones 34 | adj = adj|speye(size(adj)); 35 | end 36 | 37 | %Dulmage-Mendelsohn decomposition 38 | [useless1,p,useless2,r] = dmperm(adj); 39 | 40 | %p indicates a permutation (along rows and columns) 41 | %r is a vector indicating the component boundaries 42 | 43 | % List including the number of nodes of each component. ith entry is r(i+1)-r(i) 44 | comp_sizes = diff(r); 45 | 46 | % Number of components found. 47 | num_comps = numel(comp_sizes); 48 | 49 | % initialization 50 | comps = zeros(1,size(adj,1)); 51 | 52 | % first position of each component is set to one 53 | comps(r(1:num_comps)) = ones(1,num_comps); 54 | 55 | % cumulative sum produces a label for each component (in a consecutive way) 56 | comps = cumsum(comps); 57 | 58 | %re-order component labels according to adj. 59 | comps(p) = comps; 60 | -------------------------------------------------------------------------------- /fcn/matching_ind_und.m: -------------------------------------------------------------------------------- 1 | function M0 = matching_ind_und(CIJ) 2 | %MATCHING_IND_UND matching index 3 | % 4 | % M0 = MATCHING_IND_UND(CIJ) computes matching index for undirected 5 | % graph specified by adjacency matrix CIJ. Matching index is a measure of 6 | % similarity between two nodes' connectivity profiles (excluding their 7 | % mutual connection, should it exist). 8 | % 9 | % Inputs: CIJ, undirected adjacency matrix 10 | % 11 | % Outputs: M0, matching index matrix. 12 | % 13 | % Richard Betzel, Indiana University, 2013 14 | % 15 | CIJ0 = CIJ; 16 | K = sum(CIJ0); 17 | R = K ~= 0; 18 | N = sum(R); 19 | CIJ = CIJ0(R,R); 20 | I = ~eye(N); 21 | M = zeros(N,N); 22 | for i = 1:N 23 | 24 | c1 = CIJ(i,:); 25 | use = bsxfun(@or,c1,CIJ); 26 | use(:,i) = 0; 27 | use = use.*I; 28 | 29 | ncon1 = bsxfun(@times,use,c1); 30 | ncon2 = bsxfun(@times,use,CIJ); 31 | ncon = sum(ncon1 + ncon2,2); 32 | 33 | M(:,i) = 2*sum(ncon1 & ncon2,2)./ncon; 34 | 35 | end 36 | M = M.*I; 37 | M(isnan(M)) = 0; 38 | M0 = zeros(size(CIJ0)); 39 | M0(R,R) = M; -------------------------------------------------------------------------------- /fcn/mean_first_passage_time.m: -------------------------------------------------------------------------------- 1 | function MFPT = mean_first_passage_time(adj) 2 | % MEAN_FIRST_PASSAGE_TIME Mean first passage time 3 | % 4 | % MFPT = mean_first_passage_time(adj) 5 | % 6 | % The first passage time (MFPT) from i to j is the expected number of 7 | % steps it takes a random walker starting at node i to arrive for the 8 | % first time at node j. The mean first passage time is not a 9 | % symmetric measure: mfpt(i,j) may be different from mfpt(j,i). 10 | % 11 | % Input: 12 | % adj, Weighted/Unweighted, directed/undirected adjacency matrix 13 | % 14 | % Output: 15 | % MFPT, Pairwise mean first passage time matrix. 16 | % 17 | % 18 | % References: Goñi J, et al (2013) PLoS ONE 19 | % 20 | % Joaquin Goñi, IU Bloomington, 2012 21 | 22 | 23 | P = diag(sum(adj,2))\adj; % matrix of transition probabilities 24 | 25 | tol=10^(-3); %tolerance to find value of 1 at the eigenvector 26 | 27 | n = length(P); %number of nodes 28 | [V,D_eigen] = eig(P'); %diagonal matrix D_eigen of eigenvalues. Full matrix V whose columns are the corresponding eigenvectors so that X*V = V*D. In our case X=P'; 29 | 30 | aux = abs(diag(D_eigen)-1); 31 | index = find(aux==min(aux)); 32 | if aux(index)>tol 33 | error('cannot find eigenvalue of 1. Minimum eigenvalue value is %0.6f. Tolerance was set at %0.6f',aux(index)+1,tol); 34 | end 35 | 36 | w = V(:,index)'; %left-eigen vector associated to eigenvalue of 1. 37 | w = w/sum(w); %rescale of left-eigen vector to the sum of it (hence is now in probabilites form. The inverse of this vector is the return-times vector 38 | 39 | W = repmat(w,n,1); %convert column-vector w to a full matrix W by making copies of w. 40 | I = eye(n,n); %Identity matrix I is computed 41 | 42 | Z = inv(I-P+W); %Fundamental matrix Z is computed 43 | 44 | MFPT = (repmat(diag(Z)',n,1)-Z)./W; % this performs MFPT(i,j)=(Z(j,j)-Z(i,j))/w(j) in a matricial way. Corresponds to theorem 11.16 pag. 459 45 | % r = 1./w; %as demostrated in theorem 11.15 pag. 455. Each entry r_i is the 'mean-recurrence' or 'return-time' of state i (node i when states represent nodes of a graph) -------------------------------------------------------------------------------- /fcn/navigate.m: -------------------------------------------------------------------------------- 1 | function nav = navigate(C, MS, max_hops) 2 | 3 | % Function to perform navigation routing in a connecitivty matrix C, guided by 4 | % the metric space MS. 5 | % Inputs: 6 | % C - square connectivity matrix 7 | % MS - square symmetric matrix of distances between nodes in the metric 8 | % space 9 | % max_hops (optinal) - limits the maximum number of hops of navigation 10 | % paths 11 | % Output: 12 | % nav - structure containing: 13 | % .inputs - User specific inputs 14 | % .paths - cell matrix of navigation paths 15 | % .num_hops - number of hops of navigation paths 16 | % .pl_MS - navigation path lengths in metric space 17 | % .failed_paths - binary matrix of failed paths (failure = 1) 18 | % 19 | % Caio Seguin, University of Melbourne, 2017 20 | 21 | if nargin == 2 22 | max_hops = length(C); 23 | end 24 | 25 | components = get_components(C); 26 | 27 | if (length(unique(components)) > 1) 28 | fprintf('Warning: disconnected connectivity matrix\n'); 29 | end 30 | 31 | n = size(C, 1); 32 | 33 | nav.inputs.C = C; 34 | nav.inputs.MS = MS; 35 | nav.inputs.max_hops = max_hops; 36 | 37 | nav.paths = cell(n); 38 | nav.num_hops = zeros(n); 39 | nav.pl_MS = zeros(n); 40 | nav.failed_paths = zeros(n); nav.failed_paths(1:n+1:end) = 1; 41 | 42 | for i = 1:n 43 | for j = 1:n 44 | if (i ~= j) 45 | 46 | curr_node = i; 47 | last_node = curr_node; 48 | target = j; 49 | num_hops = 0; 50 | pl_MS = 0; 51 | nav.paths{i,j} = curr_node; 52 | 53 | while (curr_node ~= target) 54 | 55 | neighbors = find(C(curr_node,:) ~= 0); 56 | [~, min_index] = min(MS(target, neighbors)); 57 | next_node = neighbors(min_index); 58 | 59 | if isempty(next_node) || next_node == last_node || num_hops > max_hops 60 | 61 | num_hops = 0; 62 | pl_MS = 0; 63 | nav.failed_paths(i,j) = 1; 64 | 65 | break 66 | 67 | end 68 | 69 | nav.paths{i,j} = [nav.paths{i,j} next_node]; 70 | num_hops = num_hops + 1; 71 | pl_MS = MS(curr_node, next_node) + pl_MS; 72 | 73 | last_node = curr_node; 74 | curr_node = next_node; 75 | 76 | end 77 | 78 | nav.num_hops(i,j) = num_hops; 79 | nav.pl_MS(i,j) = pl_MS; 80 | 81 | end 82 | end 83 | end 84 | 85 | end -------------------------------------------------------------------------------- /fcn/path_transitivity.m: -------------------------------------------------------------------------------- 1 | function T=path_transitivity(W,transform) 2 | % PATH_TRANSITIVITY Transitivity based on shortest paths 3 | % 4 | % T = path_transitivity(W,transform) 5 | % 6 | % This function computes the density of local detours (triangles) that 7 | % are available along the shortest-paths between all pairs of nodes. 8 | % 9 | % Inputs: 10 | % 11 | % W, 12 | % unweighted/weighted undirected connection *weight* OR *length* 13 | % matrix. 14 | % 15 | % 16 | % transform, 17 | % If the input matrix is a connection *weight* matrix, specify a 18 | % transform that map input connection weights to connection 19 | % lengths. Two transforms are available. 20 | % 'log' -> l_ij = -log(w_ij) 21 | % 'inv' -> l_ij = 1/w_ij 22 | % 23 | % If the input matrix is a connection *length* matrix, do not 24 | % specify a transform (or specify an empty transform argument). 25 | % 26 | % 27 | % Output: 28 | % 29 | % T, 30 | % matrix of pairwise path transitivity. 31 | % 32 | % 33 | % Olaf Sporns, Andrea Avena-Koenigsberger and Joaquin Goñi, IU Bloomington, 2014 34 | % 35 | % References: Goñi et al (2014) PNAS doi: 10.1073/pnas.131552911 36 | % 37 | 38 | if ~exist('transform','var') 39 | transform = []; 40 | end 41 | 42 | n=length(W); 43 | m=zeros(n,n); 44 | T=zeros(n,n); 45 | 46 | for i=1:n-1 47 | for j=i+1:n 48 | x=0; 49 | y=0; 50 | z=0; 51 | for k=1:n 52 | if W(i,k)~=0 && W(j,k)~=0 && k~=i && k~=j 53 | x=x+W(i,k)+W(j,k); 54 | end 55 | if k~=j 56 | y=y+W(i,k); 57 | end 58 | if k~=i 59 | z=z+W(j,k); 60 | end 61 | end 62 | m(i,j)=x/(y+z); 63 | end 64 | end 65 | m=m+m'; 66 | 67 | [~,hops,Pmat] = distance_wei_floyd(W,transform); 68 | 69 | % --- path transitivity ---%% 70 | for i=1:n-1 71 | for j=i+1:n 72 | x=0; 73 | path = retrieve_shortest_path(i,j,hops,Pmat); 74 | K=length(path); 75 | 76 | for t=1:K-1 77 | for l=t+1:K 78 | x=x+m(path(t),path(l)); 79 | end 80 | end 81 | T(i,j)=2*x/(K*(K-1)); 82 | end 83 | end 84 | T=T+T'; 85 | -------------------------------------------------------------------------------- /fcn/retrieve_shortest_path.m: -------------------------------------------------------------------------------- 1 | 2 | function [path] = retrieve_shortest_path(s,d,SPL,B) 3 | 4 | path_length = SPL(s,d); 5 | path = nan(path_length+1,1); 6 | path(1) = s; 7 | for ind = 2:length(path); 8 | s = B(s,d); 9 | path(ind) = s; 10 | end 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /fcn/search_information.m: -------------------------------------------------------------------------------- 1 | function SI = search_information(adj,transform,has_memory) 2 | % SEARCH_INFORMATION Search information 3 | % 4 | % SI = search_information(adj,transform,has_memory) 5 | % 6 | % Computes the amount of information (measured in bits) that a random 7 | % walker needs to follow the shortest path between a given pair of nodes. 8 | % 9 | % Inputs: 10 | % 11 | % adj, 12 | % Weighted/unweighted directed/undirected 13 | % connection *weight* OR *length* matrix. 14 | % 15 | % transform, 16 | % If the input matrix is a connection *weight* matrix, specify a 17 | % transform that map input connection weights to connection 18 | % lengths. Two transforms are available. 19 | % 'log' -> l_ij = -log(w_ij) 20 | % 'inv' -> l_ij = 1/w_ij 21 | % 22 | % If the input matrix is a connection *length* matrix, do not 23 | % specify a transform (or specify an empty transform argument). 24 | % 25 | % has_memory, 26 | % This flag defines whether or not the random walker "remembers" 27 | % its previous step, which has the effect of reducing the amount 28 | % of information needed to find the next state. If this flag is 29 | % not set, the walker has no memory by default. 30 | % 31 | % 32 | % Outputs: 33 | % 34 | % SI, 35 | % pair-wise search information (matrix). Note that SI(i,j) may be 36 | % different from SI(j,i), hense, SI is not a symmetric matrix 37 | % even when adj is symmetric. 38 | % 39 | % 40 | % References: Rosvall et al. (2005) Phys Rev Lett 94, 028701 41 | % Goñi et al (2014) PNAS doi: 10.1073/pnas.131552911 42 | % 43 | % 44 | % Andrea Avena-Koenigsberger and Joaquin Goñi, IU Bloomington, 2014 45 | 46 | 47 | % Modification history 48 | % 2014 - original 49 | % 2016 - included SPL transform option and generalized for 50 | % symmetric/asymmetric networks 51 | 52 | 53 | if ~exist('transform','var') 54 | transform = []; 55 | end 56 | 57 | if ~exist('has_memory','var') 58 | has_memory = false; 59 | end 60 | 61 | N = size(adj,1); 62 | 63 | if sum(sum( triu(adj,1) + triu(adj,1)' - (adj))) < eps 64 | flag_triu = true; % matrix is symmetric (undirected network) 65 | else 66 | flag_triu = false; % matrix is not symmetric (directed network) 67 | end 68 | 69 | T = diag(sum(adj,2))\adj; 70 | [~,hops,Pmat] = distance_wei_floyd(adj,transform); 71 | 72 | SI = zeros(N,N); 73 | SI(eye(N)>0) = nan; 74 | 75 | for i = 1:N 76 | for j = 1:N 77 | if (j > i && flag_triu) || (~flag_triu && i ~= j) 78 | path = retrieve_shortest_path(i,j,hops,Pmat); 79 | lp = length(path); 80 | if flag_triu 81 | if ~isempty(path) 82 | pr_step_ff = nan(1,lp-1); 83 | pr_step_bk = nan(1,lp-1); 84 | if has_memory 85 | pr_step_ff(1) = T(path(1),path(2)); 86 | pr_step_bk(lp-1) = T(path(lp),path(lp-1)); 87 | for z=2:lp-1 88 | pr_step_ff(z) = T(path(z),path(z+1))/(1 - T(path(z-1),path(z))); 89 | pr_step_bk(lp-z) = T(path(lp-z+1),path(lp-z))/(1 - T(path(lp-z+2),path(lp-z+1))); 90 | end 91 | else 92 | for z=1:length(path)-1 93 | pr_step_ff(z) = T(path(z),path(z+1)); 94 | pr_step_bk(z) = T(path(z+1),path(z)); 95 | end 96 | end 97 | prob_sp_ff = prod(pr_step_ff); 98 | prob_sp_bk = prod(pr_step_bk); 99 | SI(i,j) = -log2(prob_sp_ff); 100 | SI(j,i) = -log2(prob_sp_bk); 101 | else 102 | SI(i,j) = inf; 103 | SI(j,i) = inf; 104 | end 105 | else 106 | if ~isempty(path) 107 | pr_step_ff = nan(1,lp-1); 108 | if has_memory 109 | pr_step_ff(1) = T(path(1),path(2)); 110 | for z=2:lp-1 111 | pr_step_ff(z) = T(path(z),path(z+1))/(1 - T(path(z-1),path(z))); 112 | end 113 | else 114 | for z=1:length(path)-1 115 | pr_step_ff(z) = T(path(z),path(z+1)); 116 | end 117 | end 118 | prob_sp_ff = prod(pr_step_ff); 119 | SI(i,j) = -log2(prob_sp_ff); 120 | else 121 | SI(i,j) = inf; 122 | end 123 | end 124 | end 125 | end 126 | end 127 | --------------------------------------------------------------------------------