├── graphical_abstract.png ├── get_traj.m ├── README.md ├── determinant_ratio.m ├── modal_controllability_continuous.m ├── optim_fun.m └── open_loop_control.m /graphical_abstract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jastiso/NetworkControl/HEAD/graphical_abstract.png -------------------------------------------------------------------------------- /get_traj.m: -------------------------------------------------------------------------------- 1 | function [ traj_dist, state_corr ] = get_traj( X, xT, nTime) 2 | %takes a high dimensional state space (output from open loop control) and 3 | %gets measure of similarity to the target (distance, and correlation) 4 | % inputs: 5 | % X : the state space. should be 4 6 | % xT : the target state. Should be 2D (here it is post freq state) 7 | % nTime : number of time points 8 | % 9 | % outputs: 10 | % traj_dist : norm of distance from target over time 11 | % state_corr : correlation with target state over time 12 | 13 | %@author JStiso, jeni.stiso@gmail.com Aug 2017 14 | 15 | % initialize trajectories and energies 16 | traj_dist = zeros(1, nTime); 17 | state_corr = zeros(1, nTime); 18 | 19 | % for every trial, get magnitudes of trajectory distance and energy 20 | 21 | % get trajectory distance ||(x(t) - x_T)|| 22 | tmp_dist = X - permute(repmat(xT, [nTime, 1, 1]), [2,3,1]); 23 | % do norm for each time 24 | for i = 1:nTime 25 | traj_dist(i) = norm(tmp_dist(:,:,i), 'fro'); 26 | % get correlation 27 | if ~isrow(squeeze(xT)) 28 | state_corr(i) = corr2(squeeze(X(:,:,i)), squeeze(xT)); 29 | else 30 | state_corr(i) = corr(squeeze(X(:,:,i)), squeeze(xT)'); 31 | end 32 | end 33 | 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NetworkControl 2 | This repository contains basic functions in MATLAB to enable to application of network control theory to complex systems. 3 | 4 | Specifically, it contains 4 functions: 5 | (1) open_loop_control: This function simulates classic open loop control x_dot(t) = Ax(t) + Bu(t) 6 | 7 | (2) optim_fun This function estimates optimal energy required to drive the system from a given initial to a given final state. Optimality is determined via constraints on distance and energy 8 | 9 | (3) modal_controlability_continuous This calculates persistant and transient controllability for continuous systems. 10 | 11 | (4) determinant_ratio This calculates the determinant ratio for a given network. This code returns a single measure for the network, thoughthe metric can be adapted as a regional measure 12 | 13 | (5) get_traj This takes the output from open_loop_control and calculates similarity measures to a target state (correlation and Frobenius norm distance) 14 | 15 | ## White Matter Network Architecture Guides Direct Electrical Stimulation through Optimal State Transitions 16 | These are key functions needed to produce results for the above paper in Cell Reports ([from journal](https://www.sciencedirect.com/science/article/pii/S2211124719310411), or [from personal website](http://www.jenniferstiso.com/publications/)) 17 | 18 | ![stim_control](graphical_abstract.png) 19 | 20 | 21 | ## Contributors (Alphabetically) 22 | * Jennifer Stiso 23 | * Tommaso Menara 24 | -------------------------------------------------------------------------------- /determinant_ratio.m: -------------------------------------------------------------------------------- 1 | function [ det_rat ] = determinant_ratio( A, driver, nondriver ) 2 | % This function calculates the deteminant ratio of a graph, given indices 3 | % of the driver and non-driver nodes. Note that there must be more driver 4 | % than non-driver nodes. See Kim et al 2017 Nat. Phys for more info. 5 | % It is important to note that this metric showed theoretical validity for 6 | % situations where there were more driver than non driver nodes, the driver 7 | % and non-driver nodes were not overlapping, and where the states were zero 8 | % mean. It is important to note when these assumptions are violated. 9 | % 10 | % Since determinants are computationally difficult to calculate, we 11 | % calculate the average determinant ratio across all non-driver nodes as 12 | % the inverse of trace of A21xA21'. See Kim et. al. Net Phys for more 13 | % details. 14 | % 15 | % Input 16 | % A An NxN adjacency/connectivity matrix 17 | % drivers An Nx1 logical or 0-1 vector, where 1 indicates the position 18 | % of a driver node 19 | % nondrivers An Nx1 logical or 0-1 vector, where 1 indicates the 20 | % position of the non-driver node. nondrivers can be equal to 21 | % ~drivers, but does not have to be 22 | 23 | 24 | % Outputs 25 | % det_rat The detminant ratio of the specified control set up 26 | 27 | % @author JStiso 28 | 29 | % check that there are more drivers than non-drivers 30 | if sum(nondriver) > sum(driver) 31 | warning('You should not have more non-driver than driver nodes') 32 | return 33 | end 34 | 35 | % get A21 36 | A21 = A(driver,nondriver)'; 37 | Q = A21*A21'; 38 | det_rat = trace(inv(Q)); 39 | 40 | end 41 | 42 | -------------------------------------------------------------------------------- /modal_controllability_continuous.m: -------------------------------------------------------------------------------- 1 | function [ phi_p, phi_t ] = modal_continuous_persistent( V, lambda, i, dt, thresh) 2 | % Calculate persistent and transient modal controllability of node i. 3 | % Intuitively, nodes with high persistent controllability will result in 4 | % large perturbations to slow modes of the system when stimulated, and 5 | % nodes with high transient controllability will result in large 6 | % perturbations to fast modes of the system when stimulated 7 | % Inputs: 8 | % V The NxN matrix conatining eigenvectors of your system, where N 9 | % is the number of nodes 10 | % lambda The Nx1 vector of eigenvalues for your system 11 | % i This index of the node you wish to calculate the 12 | % controllability of 13 | % dt The time step of the system 14 | % thresh The thresh hold for calculating persistent and transient modal 15 | % controllability. For example, if thresh is .1, this will 16 | % use the 10% slowest modes for persistent controllability, and 17 | % the 10% fastest modes for transient controllability. 18 | 19 | % Outputs: 20 | % phi_p Persistent controllability of the node (should be between 0 and 21 | % 1) 22 | % phi_t Transient controllability (should be between 0 and 1) 23 | 24 | % @author JStiso 25 | 26 | 27 | % convert to discrete lambda - comment out if system if already discrete 28 | discrete_lambda = exp(lambda.*dt); 29 | % get only the X% largest values 30 | [modes, idx] = sort(discrete_lambda); 31 | cutoff = round(numel(modes)*thresh); 32 | transient_modes = modes(1:cutoff); 33 | persistent_modes = modes((end-cutoff+1):end); 34 | % get controllability 35 | phi_p = sum((1 - (persistent_modes).^2).*(V(i,idx((numel(idx)-cutoff+1):end)).^2)'); 36 | phi_t = sum((1 - (transient_modes).^2).*(V(i,idx(1:cutoff)).^2)'); 37 | 38 | end 39 | -------------------------------------------------------------------------------- /optim_fun.m: -------------------------------------------------------------------------------- 1 | % compute optimal inputs/trajectories 2 | % Fabio, Tommy September 2017 3 | % 4 | % -------------- Change Log ------------- 5 | % JStiso April 2018 6 | % Changed S and B to be an input, rather than something defined internally 7 | 8 | % Inputs: 9 | % A (NxN) Structural connectivity matrix 10 | % B (NxN) Input matrix: selects which nodes to put input into. Define 11 | % so there is a 1 on the diagonal of elements you want to add input to, 12 | % and 0 otherwise 13 | % S (NxN) Selects nodes whose distance you want to constrain, Define so 14 | % that there is a 1 on the diagonal of elements you want to 15 | % constrain, and a zero otherwise 16 | % T Time horizon: how long you want to control for. Too large will give 17 | % large error, too short will not give enough time for control 18 | % rho weights energy and distance constraints. Small rho leads to larger 19 | % energy 20 | % x0 (Nx1) the intial state of your system 21 | % xf (Nx1) the final state of your system 22 | 23 | function [X_opt, U_opt, n_err] = optim_fun(A, T, B, x0, xf, rho, S) 24 | 25 | n = size(A,2); 26 | 27 | %target_nodes_number = find(xf(:,1)); 28 | 29 | % S = zeros(n); 30 | % for i = 1:size(target_nodes_number) 31 | % S(target_nodes_number(i), target_nodes_number(i)) = 1; 32 | % end 33 | Sbar = eye(n) - S; 34 | 35 | Atilde = [A -B*B'/(2*rho) ; -2*S -A']; 36 | 37 | M = expm(Atilde*T); 38 | M11 = M(1:n,1:n); 39 | M12 = M(1:n,n+1:end); 40 | M21 = M(n+1:end,1:n); 41 | M22 = M(n+1:end,n+1:end); 42 | 43 | N = Atilde\(M-eye(size(Atilde))); 44 | c = N*[zeros(n);S]*2*xf; 45 | c1 = c(1:n); 46 | c2 = c(n+1:end); 47 | 48 | p0 = pinv([S*M12;Sbar*M22]) * (-[S*M11;Sbar*M21]*x0 - [S*c1;Sbar*c2] + [S*xf;zeros(n,1)]); 49 | 50 | n_err = norm([S*M12;Sbar*M22]*p0 - (-[S*M11;Sbar*M21]*x0 - [S*c1;Sbar*c2] + [S*xf;zeros(n,1)])); % norm(error) 51 | 52 | sys_xp = ss(Atilde,[zeros(n);S],eye(2*n),0); 53 | 54 | STEP = 0.001; 55 | t = 0:STEP:T; 56 | 57 | U = []; 58 | while size(U,1) < length(t) 59 | U = [U;2*xf']; 60 | end 61 | 62 | [Y,tt,xp] = lsim(sys_xp,U,t,[x0;p0]); 63 | 64 | % sys = ss(A,B*B'/(2*rho),eye(n),0); 65 | % [Y,T,X] = lsim(sys,-xp(:,n+1:end),tt,x0); 66 | 67 | U_opt = []; 68 | for i = 1:length(t) 69 | U_opt(i,:) = -(1/(2*rho))*B'*xp(i,n+1:end)'; 70 | end 71 | 72 | X_opt = xp; 73 | end 74 | -------------------------------------------------------------------------------- /open_loop_control.m: -------------------------------------------------------------------------------- 1 | function [ x ] = openLoopControl( A, B, xi, U) 2 | % This function caclulates the trajectory for the network given our model 3 | % if there are no constraints, and the target state is unknown, using the 4 | % control equation precess dx = Ax(t) + BU(t). x(t) is the state vector, A is 5 | % the adjacency matrix, U(t) is the time varying input as specified by the 6 | % user, and B selects the control set (stimulating electrodes) 7 | % 8 | % Inputs 9 | % A : NxN state matrix, where N is the number of nodes in your 10 | % network (for example, a structural connectivity matrix 11 | % constructed from DTI). A should be stable to prevent 12 | % uncontrolled trajectories. 13 | % 14 | % B : NxN input matrix, where N is the number of nodes. B 15 | % selects where you want your input energy to be applied to. 16 | % For example, if B is the Identity matrix, then input energy 17 | % will be applied to all nodes in the network. If B is a 18 | % matrix of zeros, but B(1,1) = 1. then energy will only be 19 | % applied at the first node. 20 | % 21 | % xi : NxM initial state of your system where N is the number of 22 | % nodes, and M is the number of independent states (ie 23 | % frequency bands). xi MUST have N rows, however, the number 24 | % of state measurements can change (and can be equal to 1). 25 | % 26 | % U : NxMxT matrix of Energy, where N is the number of nodes, M 27 | % is the number of state measurements, and T is the number of 28 | % time points. For example, if you want to simulate the 29 | % trajectory resulting from stimulation, U could have 30 | % log(StimFreq)*StimAmp*StimDur as every element. You can 31 | % also enter U's that vary with time, or are different 32 | % accross frequency bands. 33 | % 34 | % Outputs 35 | % x : x is the NxMxT trajectory that results from simulating 36 | % x(t+1) = Ax(t) + Bu(t) the equation with the parameters 37 | % above. 38 | % 39 | % @author JStiso 40 | % June 2017 41 | 42 | % Simulate trajectory 43 | T = size(U,3); 44 | N = size(A,1); 45 | M = size(xi,2); 46 | % initialize x 47 | x = zeros(N, M, T); 48 | xt = xi; 49 | for t = 1:T 50 | x(:,:,t) = xt; 51 | dx = A*xt + B*squeeze(U(:,:,t)); % state equation 52 | xt = xt + dx; 53 | end 54 | end 55 | 56 | --------------------------------------------------------------------------------