├── PowerlawSampler.m ├── MultiplexDependencyMatrix.m ├── ind2subarray.m ├── TemporalDependencyMatrix.m ├── DirichletSampler.m ├── TemporalCPDependencyMatrix.m ├── HeterogeneousMultiplexDependencyMatrix.m ├── subarray2ind.m ├── MultiaspectDependencyMatrix.m ├── example_script.m ├── DirichletNullDistribution.m ├── README.md ├── DirichletDCSBMBenchmark.m ├── DCSBMNetworkGenerator.m ├── ConvergenceTest.m └── PartitionGenerator.m /PowerlawSampler.m: -------------------------------------------------------------------------------- 1 | function x=PowerlawSampler(n,t,x_min,x_max) 2 | % Sample values from a truncated powerlaw distribution 3 | % 4 | % Input: 5 | % 6 | % n: number of samples 7 | % 8 | % t: exponent 9 | % 10 | % x_min: minimum cut-off 11 | % 12 | % x_max: maximum cut-off 13 | % 14 | % Output: 15 | % 16 | % x: sampled values 17 | % 18 | % Version: 2.0.0 19 | % Date: Thu 11 Jul 2019 15:24:15 CEST 20 | % Author: Lucas Jeub 21 | % Email: lucasjeub@gmail.com 22 | % 23 | % 24 | % References: 25 | % 26 | % [1] Generative benchmark models for mesoscale structure in multilayer 27 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 28 | % Porter. arXiv1:608.06196. 29 | % 30 | % Citation: 31 | % 32 | % If you use this code, please cite as 33 | % Lucas G. S. Jeub and Marya Bazzi 34 | % "A generative model for mesoscale structure in multilayer networks 35 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 36 | 37 | 38 | y=rand(n,1); 39 | if t~=-1 40 | x=((x_max^(t+1)-x_min^(t+1))*y+x_min^(t+1)).^(1/(t+1)); 41 | else 42 | x=x_min*(x_max/x_min).^y; 43 | end 44 | 45 | end 46 | -------------------------------------------------------------------------------- /MultiplexDependencyMatrix.m: -------------------------------------------------------------------------------- 1 | function P=MultiplexDependencyMatrix(n_layers,p) 2 | % Generate a uniform multiplex dependency matrix 3 | % 4 | % Input: 5 | % 6 | % n_layers: number of layers 7 | % 8 | % p: probability for a state node to copy its community assignment from 9 | % corresponding state nodes in other layers 10 | % 11 | % Output: 12 | % 13 | % P: dependency matrix for use with PartitionGenerator 14 | % 15 | % Note that p<=1 16 | % 17 | % 18 | % Version: 2.0.0 19 | % Date: Thu 11 Jul 2019 15:24:15 CEST 20 | % Author: Lucas Jeub 21 | % Email: lucasjeub@gmail.com 22 | % 23 | % References: 24 | % 25 | % [1] Generative benchmark models for mesoscale structure in multilayer 26 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 27 | % Porter. arXiv1:608.06196. 28 | % 29 | % Citation: 30 | % 31 | % If you use this code, please cite as 32 | % Lucas G. S. Jeub and Marya Bazzi 33 | % "A generative model for mesoscale structure in multilayer networks 34 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 35 | % 36 | 37 | P(1:n_layers,1:n_layers)=p/(n_layers-1); 38 | P=P-diag(diag(P)); 39 | 40 | end 41 | -------------------------------------------------------------------------------- /ind2subarray.m: -------------------------------------------------------------------------------- 1 | function [sub] = ind2subarray(shape,ind) 2 | %IND2SUBARRAY Subscripts from linear index. Same as IND2SUB but 3 | % returns results as an array instead of separate output arguments. 4 | % 5 | % Version: 2.0.0 6 | % Date: Thu 11 Jul 2019 15:24:15 CEST 7 | % Author: Lucas Jeub 8 | % Email: lucasjeub@gmail.com 9 | % 10 | % 11 | % References: 12 | % 13 | % [1] Generative benchmark models for mesoscale structure in multilayer 14 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 15 | % Porter. arXiv1:608.06196. 16 | % 17 | % Citation: 18 | % 19 | % If you use this code, please cite as 20 | % Lucas G. S. Jeub and Marya Bazzi 21 | % "A generative model for mesoscale structure in multilayer networks 22 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 23 | 24 | shape = double(shape); 25 | dims = length(shape); 26 | sub=zeros(length(ind),length(shape)); 27 | 28 | k = cumprod(shape); 29 | if ind>k(end) 30 | error('MultilayerBenchmark:ind2subarray:IndexOutOfRange','Index out of range.'); 31 | end 32 | for i = dims:-1:2, 33 | subi = rem(ind-1, k(i-1)) + 1; 34 | subj = (ind - subi)/k(i-1) + 1; 35 | sub(:,i) = double(subj); 36 | ind = subi; 37 | end 38 | sub(:,1)=ind; 39 | 40 | end 41 | -------------------------------------------------------------------------------- /TemporalDependencyMatrix.m: -------------------------------------------------------------------------------- 1 | function P=TemporalDependencyMatrix(n_layers,p) 2 | % Generate a uniform temporal dependency matrix 3 | % 4 | % Input: 5 | % 6 | % n_layers: number of layers 7 | % 8 | % p: probability for a state node to copy its community assignment from 9 | % corresponding state node in previous layer 10 | % 11 | % Output: 12 | % 13 | % P: dependency matrix for use with PartitionGenerator 14 | % 15 | % Note that p<=1 16 | % 17 | % Version: 2.0.0 18 | % Date: Thu 11 Jul 2019 15:24:15 CEST 19 | % Author: Lucas Jeub 20 | % Email: lucasjeub@gmail.com 21 | % 22 | % References: 23 | % 24 | % [1] Generative benchmark models for mesoscale structure in multilayer 25 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 26 | % Porter. arXiv1:608.06196. 27 | % 28 | % Citation: 29 | % 30 | % If you use this code, please cite as 31 | % Lucas G. S. Jeub and Marya Bazzi 32 | % "A generative model for mesoscale structure in multilayer networks 33 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 34 | 35 | if p>1||p<0 36 | error('MultilayerBenchmark:TemporalDependencies:p',... 37 | 'Copying probability p out of range') 38 | end 39 | 40 | P=zeros(n_layers); 41 | 42 | for i=1:n_layers-1 43 | P(i,i+1)=p; 44 | end 45 | 46 | end 47 | -------------------------------------------------------------------------------- /DirichletSampler.m: -------------------------------------------------------------------------------- 1 | function weights=DirichletSampler(theta,q,nc) 2 | % Sample weights for community null-distriubtion from a Dirichlet distribution 3 | % 4 | % Input: 5 | % 6 | % theta: shape parameter for Dirichlet distribution 7 | % 8 | % q: activation probability (probability for a given community 9 | % weight to be non-zero) 10 | % 11 | % nc: number of communities 12 | % 13 | % Output: 14 | % 15 | % weights: sampled weigths (i.e. probabilities of a categorical 16 | % distribution) 17 | % 18 | % 19 | % Version: 2.0.0 20 | % Date: Thu 11 Jul 2019 15:24:15 CEST 21 | % Author: Lucas Jeub 22 | % Email: lucasjeub@gmail.com 23 | % 24 | % 25 | % References: 26 | % 27 | % [1] Generative benchmark models for mesoscale structure in multilayer 28 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 29 | % Porter. arXiv1:608.06196. 30 | % 31 | % Citation: 32 | % 33 | % If you use this code, please cite as 34 | % Lucas G. S. Jeub and Marya Bazzi 35 | % "A generative model for mesoscale structure in multilayer networks 36 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 37 | 38 | weights=zeros(nc,1); 39 | active=find(rand(nc,1)<=q); 40 | 41 | weights(active)=gamrnd(theta,1,length(active),1); 42 | weights=weights./sum(weights); 43 | 44 | end 45 | -------------------------------------------------------------------------------- /TemporalCPDependencyMatrix.m: -------------------------------------------------------------------------------- 1 | function P=TemporalCPDependencyMatrix(n_layers,p, pc, nc) 2 | % Generate a uniform temporal dependency matrix 3 | % 4 | % Input: 5 | % 6 | % n_layers: number of layers 7 | % 8 | % p: probability for a state node to copy its community assignment from 9 | % corresponding state node in previous layer when not at change point 10 | % 11 | % pc: probability for a state node to copy its community assignment from 12 | % corresponding state node in previous layer when at change point 13 | % 14 | % nc: number of changepoints 15 | % 16 | % Output: 17 | % 18 | % P: dependency matrix for use with PartitionGenerator 19 | % 20 | % Note that p,pc<=1 21 | % 22 | % Version: 2.0.0 23 | % Date: Thu 11 Jul 2019 15:24:15 CEST 24 | % Author: Lucas Jeub 25 | % Email: lucasjeub@gmail.com 26 | % 27 | % References: 28 | % 29 | % [1] Generative benchmark models for mesoscale structure in multilayer 30 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 31 | % Porter. arXiv1:608.06196. 32 | % 33 | % Citation: 34 | % 35 | % If you use this code, please cite as 36 | % Lucas G. S. Jeub and Marya Bazzi 37 | % "A generative model for mesoscale structure in multilayer networks 38 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 39 | 40 | if p>1||p<0 41 | error('MultilayerBenchmark:TemporalDependencies:p',... 42 | 'Copying probability p out of range') 43 | end 44 | 45 | P=zeros(n_layers); 46 | cp_skip=ceil(n_layers/(nc+1)); 47 | 48 | for i=1:n_layers-1 49 | P(i,i+1)=p; 50 | end 51 | 52 | for i=cp_skip:cp_skip:n_layers-1 53 | P(i,i+1)=pc; 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /HeterogeneousMultiplexDependencyMatrix.m: -------------------------------------------------------------------------------- 1 | function P=HeterogeneousMultiplexDependencyMatrix(n_layers,n_blocks,p_in,p_out) 2 | % Generate a heterogeneous multiplex dependency matrix 3 | % 4 | % Input: 5 | % 6 | % n_layers: number of layers 7 | % 8 | % n_blocks: number of blocks 9 | % 10 | % p_in: probability for a state node to copy its community assignment from 11 | % corresponding state nodes in other layers in the same block 12 | % 13 | % p_out: probability for a state node to copy its community assignment 14 | % from corresponding state nodes in other layers in different blocks 15 | % 16 | % Output: 17 | % 18 | % P: dependency matrix for use with PartitionGenerator 19 | % 20 | % Note that p_in+p_out<=1 21 | % 22 | % 23 | % Version: 2.0.0 24 | % Date: Thu 11 Jul 2019 15:24:15 CEST 25 | % Author: Lucas Jeub 26 | % Email: lucasjeub@gmail.com 27 | % 28 | % References: 29 | % 30 | % [1] Generative benchmark models for mesoscale structure in multilayer 31 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 32 | % Porter. arXiv1:608.06196. 33 | % 34 | % Citation: 35 | % 36 | % If you use this code, please cite as 37 | % Lucas G. S. Jeub and Marya Bazzi 38 | % "A generative model for mesoscale structure in multilayer networks 39 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 40 | % 41 | block_size=floor(n_layers/n_blocks); 42 | S=ones(n_layers,1)*n_blocks; 43 | for i=1:n_blocks 44 | S((1:block_size)+(i-1)*block_size)=i; 45 | end 46 | P=zeros(n_layers,n_layers); 47 | for i=1:n_blocks 48 | ind=S==i; 49 | P(ind,ind)=p_in/(sum(ind)-1); 50 | P(~ind,ind)=p_out/sum(~ind); 51 | end 52 | P=P-diag(diag(P)); 53 | 54 | end 55 | -------------------------------------------------------------------------------- /subarray2ind.m: -------------------------------------------------------------------------------- 1 | function ndx = subarray2ind(shape,suba) 2 | %SUBARRAY2IND Linear index from multiple subscripts. 3 | % Same as SUB2IND but takes array as input rather than multiple arguments. 4 | % 5 | % Version: 2.0.0 6 | % Date: Thu 11 Jul 2019 15:24:15 CEST 7 | % Author: Lucas Jeub 8 | % Email: lucasjeub@gmail.com 9 | % 10 | % 11 | % References: 12 | % 13 | % [1] Generative benchmark models for mesoscale structure in multilayer 14 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 15 | % Porter. arXiv1:608.06196. 16 | % 17 | % Citation: 18 | % 19 | % If you use this code, please cite as 20 | % Lucas G. S. Jeub and Marya Bazzi 21 | % "A generative model for mesoscale structure in multilayer networks 22 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 23 | 24 | shape = double(shape); 25 | dims = length(shape); 26 | if size(suba,2)~=dims 27 | error('MultilayerBenchmark:subarray2ind:DimensionMismatch','The subscript dimension does not match the array size.'); 28 | end 29 | 30 | 31 | if any(min(suba(:,1)) < 1) || any(max(suba(:,1)) > shape(1)) 32 | %Verify subscripts are within range 33 | error('MultilayerBenchmark:subarray2ind:IndexOutOfRange','Subscript out of range.'); 34 | end 35 | 36 | ndx = double(suba(:,1)); 37 | %Compute linear indices 38 | k = cumprod(shape); 39 | for i = 2:dims 40 | %%Input checking 41 | if (any(min(suba(:,i)) < 1)) || (any(max(suba(:,i)) > shape(i))) 42 | %Verify subscripts are within range 43 | error('MultilayerBenchmark:subarray2ind:IndexOutOfRange','Subscript out of range.'); 44 | end 45 | ndx = ndx + (double(suba(:,i))-1)*k(i-1); 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /MultiaspectDependencyMatrix.m: -------------------------------------------------------------------------------- 1 | function P=MultiaspectDependencyMatrix(layers, p, type) 2 | % Generate a block-multiplex dependency matrix 3 | % 4 | % Input: 5 | % 6 | % layers: array such that layers(i) is the number of layers in the ith aspect 7 | % 8 | % p: vector of probabilities where p(i) is the probability for a state node to 9 | % copy its community assignment from corresponding state nodes in the ith 10 | % aspect 11 | % 12 | % type: string specifying the type of dependency for each aspect. The 13 | % possible types are 'c' (or 'm') for categorical (multiplex) dependency 14 | % and 'o' (or 't') for ordinal (temporal) dependency. 15 | % 16 | % Output: 17 | % 18 | % P: Dependency matrix for use with PartitionGenerator 19 | % 20 | % Note that sum(p)<=1 21 | % 22 | % Version: 2.0.0 23 | % Date: Thu 11 Jul 2019 15:24:15 CEST 24 | % Author: Lucas Jeub 25 | % Email: lucasjeub@gmail.com 26 | % 27 | % References: 28 | % 29 | % [1] Generative benchmark models for mesoscale structure in multilayer 30 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 31 | % Porter. arXiv1:608.06196. 32 | % 33 | % Citation: 34 | % 35 | % If you use this code, please cite as 36 | % Lucas G. S. Jeub and Marya Bazzi 37 | % "A generative model for mesoscale structure in multilayer networks 38 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 39 | 40 | if sum(p)>1 41 | error('MultilayerBenchmark:MultiplexTemporalMatrix:probability',... 42 | 'Sum of copying probabilities>1'); 43 | end 44 | 45 | P = zeros(prod(layers)); 46 | for a = 1:numel(layers) 47 | P = P + kron(kron(eye(prod(layers(a+1:end))),coupling(layers(a), type(a), p(a))),... 48 | eye(prod(layers(1:a-1)))); 49 | end 50 | 51 | end 52 | 53 | function C = coupling(size, type, p) 54 | switch type 55 | case {'m', 'c'} 56 | C = ones(size)-eye(size); 57 | C = C .* p/(size-1); 58 | case {'t', 'o'} 59 | C = diag(ones(size-1,1),1); 60 | C = C * p; 61 | otherwise 62 | error('unknown coupling type %s', type) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /example_script.m: -------------------------------------------------------------------------------- 1 | % Example use of DirichletDCSBMBenchmark for multiplex and temporal multilayer networks 2 | % 3 | % References: 4 | % 5 | % [1] Generative benchmark models for mesoscale structure in multilayer 6 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 7 | % Porter. arXiv1:608.06196. 8 | % 9 | % Citation: 10 | % 11 | % If you use this code, please cite as 12 | % Lucas G. S. Jeub and Marya Bazzi 13 | % "A generative model for mesoscale structure in multilayer networks 14 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 15 | 16 | 17 | 18 | % EXAMPLE 1: Multiplex 19 | % Multiplex example with uniform interlayer dependencies between all pairs 20 | % of layers (interlayer depencency tensor as in FIG 3b of [1] and model parameter 21 | % choices as in FIG 5 and FIG 8 of [1] for specific values of p and mu) 22 | 23 | n_layers=15; 24 | n_nodes=1000; 25 | p = 0.95; mu = 0.1; 26 | 27 | L = MultiplexDependencyMatrix(n_layers,p); 28 | [A,S]=DirichletDCSBMBenchmark(n_nodes,n_layers,'r',L,... 29 | 'UpdateSteps',200,'theta',1,'communities',10,'q',1,... 30 | 'exponent',-2,'kmin',3,'kmax',150,'mu',mu,'maxreject',100); 31 | 32 | % EXAMPLE 2: TEMPORAL 33 | % Temporal example with uniform interlayer dependencies between successive 34 | % layers (interlayer depencency tensor as in FIG 3a of [1] and model parameter 35 | % choices as in FIG 4 and FIG 9 of [1] for specific values of p and mu) 36 | 37 | n_layers = 100; 38 | n_nodes = 150; 39 | p = 0.95; mu = 0.1; 40 | 41 | L = TemporalDependencyMatrix(n_layers,p); 42 | [A,S]=DirichletDCSBMBenchmark(n_nodes,n_layers,'o',L,... 43 | 'UpdateSteps',1,'theta',1,'communities',5,'q',1,... 44 | 'exponent',-2,'kmin',3,'kmax',30,'mu',mu,'maxreject',100); 45 | 46 | 47 | % EXAMPLE 3: TEMPORAL WITH CHANGE POINTS 48 | % Temporal example with nonuniform interlayer dependencies between successive 49 | % layers (interlayer depencency tensor as in FIG 3a of [1] and model parameter 50 | % choices as in FIG 10 of [1] for specific values of p and mu) 51 | 52 | n_layers = 100; 53 | n_nodes = 150; 54 | 55 | p = 0.95; p_change = 0; 56 | p_CP = p*ones(n_layers-1,1); 57 | p_CP(25) = p_change; p_CP(50) = p_change; p_CP(75) = p_change; 58 | 59 | L = diag(p_CP,1); 60 | 61 | [A,S]=DirichletDCSBMBenchmark(n_nodes,n_layers,'o',L,... 62 | 'UpdateSteps',1,'theta',1,'communities',5,'q',1,... 63 | 'exponent',-2,'kmin',3,'kmax',30,'mu',mu,'maxreject',100); 64 | -------------------------------------------------------------------------------- /DirichletNullDistribution.m: -------------------------------------------------------------------------------- 1 | function distribution = DirichletNullDistribution(layers,varargin) 2 | % Generate a random categorical null distribution sampled from a symmetric Dirichlet distribution 3 | % 4 | % The null distributions for different layers are independent samples from 5 | % the same Dirichlet distribution. 6 | % 7 | % Note that the general format for a null distribution is 8 | % 9 | % community_assignment=function(node) 10 | % 11 | % where node is a row vector specifying a state node with format 12 | % '[node_id,aspect_1,...,aspect_d]'. Here we assume that the null 13 | % distribution is the same for all state nodes in a layer and hence the 14 | % first index is ignored. 15 | % 16 | % Input: 17 | % 18 | % layers: Vector of the form [l_1,...,l_d] specifying the size of each 19 | % aspect of the mutlilayer network 20 | % 21 | % Options: 22 | % 23 | % theta: [default: 1] Concentration parameter for the symmetric Dirichlet 24 | % distribution 25 | % 26 | % communities: [default: 10] Number of community labels 27 | % 28 | % q: [default: 1] Probability for a community to be active in a layer 29 | % (i.e. have a non-zero probability to be sampled) 30 | % 31 | % Output: 32 | % 33 | % distribution: Function that takes a state node 34 | % (format: [node,aspect_1,...,aspect_d] ) and returns a random 35 | % community assignment from 1,...,'communities' 36 | % 37 | % Version: 2.0.0 38 | % Date: Thu 11 Jul 2019 15:24:15 CEST 39 | % Author: Lucas Jeub 40 | % Email: lucasjeub@gmail.com 41 | % 42 | % 43 | % References: 44 | % 45 | % [1] Generative benchmark models for mesoscale structure in multilayer 46 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 47 | % Porter. arXiv1:608.06196. 48 | % 49 | % Citation: 50 | % 51 | % If you use this code, please cite as 52 | % Lucas G. S. Jeub and Marya Bazzi 53 | % "A generative model for mesoscale structure in multilayer networks 54 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 55 | 56 | parseArgs=inputParser(); 57 | addParameter(parseArgs,'theta',1); 58 | addParameter(parseArgs,'communities',10); 59 | addParameter(parseArgs,'q',1); 60 | parse(parseArgs,varargin{:}); 61 | options=parseArgs.Results; 62 | 63 | % set up null distribution 64 | weights=zeros([options.communities,layers]); 65 | n_layers=prod(layers); 66 | % store probabilities for each layer sampled from dirichlet distribution 67 | % (flattened format) 68 | for i=1:n_layers 69 | weights(:,i)=cumsum(DirichletSampler(options.theta,options.q,options.communities)); 70 | end 71 | % return index of first cummulative weight for layer that is bigger than 72 | % rand() (seems to be the fastest way to do this in Matlab) 73 | distribution=@(node) find(weights(:,subarray2ind(layers,node(2:end)))>rand(),1); 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## MultilayerBenchmark Version 2.0 2 | ### released July 2019 3 | 4 | Please cite this code as 5 | Lucas G. S. Jeub and Marya Bazzi 6 | *"A generative model for mesoscale structure in multilayer networks implemented 7 | in MATLAB,"* https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019) 8 | 9 | This package consists of code for the generative model in [1]. It allows a user to generate multilayer networks with planted mesoscale structure (e.g., community structure) in a principled and customisable way. 10 | 11 | Our code consists of a main ```DirichletDCSBMBenchmark.m``` file which contains two main subroutines: 12 | 13 | 1. ```PartitionGenerator.m``` 14 | which constructs a planted multilayer partition, and 15 | 2. ```DCSBMNetworkGenerator.m ``` 16 | which constructs a multilayer network for a given planted multilayer partition. 17 | 18 | To use this code, the minimal input that needs to be specified by a user is: 19 | 20 | - number of nodes in each layer 21 | - number of layers, and 22 | - an interlayer dependency tensor that specifies the desired dependency structure between layers (note that the order of the layers, whenever present, needs to be respected in the interlayer dependency tensor). We include three useful examples of interlayer dependency tensors in ```TemporalDependencyMatrix.m```, ```MultiplexDependencyMatrix.m```, and ```BlockMultiplexDependencyMatrix.m``` 23 | 24 | The subroutines (1) and (2) have various parameters (which we set to a default parameter choice) that a user can modify as needed. For example, one can: 25 | 26 | - vary the number of nodes in each layer 27 | - vary the minimum and maximum expected degrees in each layer 28 | - vary the mixing parameter in the planted partition network model 29 | - vary the expected number of communities in the multilayer partition 30 | - control which community labels are present or absent in each layer 31 | - include ordered (e.g., time) and unordered aspects (e.g., social media platforms) in the same multilayer partition 32 | - vary the parameters of the Dirichlet null distribution to alter expected community sizes in the multilayer partition 33 | - use a "null distribution" other than the Dirichlet null distribution in ```PartitionGenerator.m``` 34 | 35 | Furthermore, a user can use any monolayer network model with a planted partition other than DCSBM by using a function other than ```DCSBMNetworkGenerator.m ``` to generated edges for a given planted partition. 36 | 37 | Importantly, the subroutines (1) and (2) are carried out successively in our code and not in parallel, and each can be modified independently as needed. 38 | 39 | Note that Version 1.0 and 2.0 of the code only generate (interdependent) intralayer edges for a given multilayer partition. One can modify it to generate interlayer and/or intralayer edges (see Section 4 in [1]). 40 | 41 | More extensive documentation is provided in each function and example use of this code is provided in ```examples_script.m```. See also [1] for a detailed explanation of our generative model (see in particular Section 3 "Generating Sampled Multilayer Partitions" and Section 4 "Sampling Network Edges"). 42 | 43 | ## Changes in Version 2.0 44 | 45 | - added support for sampling multilayer partitions with a mix of ordered and unordered aspects using a combination of sequential and pseudo-Gibbs sampling 46 | 47 | - added function to construct multiaspect dependency tensors with a mix of multiplex and temporal aspects 48 | 49 | - added function to plot convergence test statistics 50 | 51 | 52 | ## References: 53 | 54 | [1] Generative benchmark models for mesoscale structure in multilayer networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. Porter. arXiv:1608.06196. 55 | 56 | ## Acknowledgments: 57 | 58 | A special thank you to Sam D. Howison, Mason A. Porter, and Alex Arenas for contributing ideas that have helped develop our generative model for mesoscale structure in multilayer networks. 59 | -------------------------------------------------------------------------------- /DirichletDCSBMBenchmark.m: -------------------------------------------------------------------------------- 1 | function [A,S]=DirichletDCSBMBenchmark(nodes,layers,types,dependencyMatrix,varargin) 2 | % Generate multi-aspect multilayer networks with a DCSBM planted partition model 3 | % 4 | % Input: 5 | % 6 | % nodes: Number of physical nodes 7 | % 8 | % layers: Number of layers for each aspect 9 | % 10 | % types: 'char' vector specifying the update type for each aspect, 'o' for 11 | % ordered and 'r' for random. 12 | % 13 | % dependencyMatrix: matrix of copying probabilities 14 | % 15 | % Output: 16 | % 17 | % A: cell array of adjacency matrices for each layer 18 | % 19 | % S: planted multilayer partition 20 | % 21 | % Options: 22 | % 23 | % UpdateSteps: [100] number of Gibbs updates before returning partition 24 | % 25 | % theta: [1] concentration parameter for Dirichlet null distribution 26 | % 27 | % communities: [10] number of communities 28 | % 29 | % q: [1] probability for a community to be active in a given layer 30 | % 31 | % exponent: [-3] powerlaw exponent for expected degree distribution 32 | % 33 | % kmin: [3] minimum expected degree 34 | % 35 | % kmax: [50] maximum expected degree 36 | % 37 | % mu: [0.1] fraction of random edges 38 | % 39 | % maxreject: [100] maximum number of rejections before bailing out and 40 | % issuing a warning (the resulting network has less than the desired 41 | % number of edges) 42 | % 43 | % see also: PartitionGenerator, DirichletNullDistribution, 44 | % DCSBMNetworkGenerator, MultiplexMatrix, TemporalMatrix, 45 | % BlockMultiplexMatrix 46 | % 47 | % Version: 2.0.0 48 | % Date: Thu 11 Jul 2019 15:24:15 CEST 49 | % Author: Lucas Jeub 50 | % Email: lucasjeub@gmail.com 51 | % 52 | % References: 53 | % 54 | % [1] Generative benchmark models for mesoscale structure in multilayer 55 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 56 | % Porter. arXiv1:608.06196. 57 | % 58 | % Citation: 59 | % 60 | % If you use this code, please cite as 61 | % Lucas G. S. Jeub and Marya Bazzi 62 | % "A generative model for mesoscale structure in multilayer networks 63 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 64 | 65 | 66 | % set defaults for all options 67 | parsePartitionOptions=inputParser(); 68 | parsePartitionOptions.KeepUnmatched=true; 69 | addParameter(parsePartitionOptions,'UpdateSteps',[]); 70 | addParameter(parsePartitionOptions,'InitialPartition',[]) 71 | parse(parsePartitionOptions,varargin{:}); 72 | PartitionOptions=NonDefaultOptions(parsePartitionOptions); 73 | 74 | parseNullOptions=inputParser(); 75 | parseNullOptions.KeepUnmatched=true; 76 | addParameter(parseNullOptions,'theta',[]); 77 | addParameter(parseNullOptions,'communities',[]); 78 | addParameter(parseNullOptions,'q',[]); 79 | parse(parseNullOptions,parsePartitionOptions.Unmatched); 80 | NullOptions=NonDefaultOptions(parseNullOptions); 81 | 82 | parseNetworkOptions=inputParser(); 83 | addParameter(parseNetworkOptions,'exponent',[]); 84 | addParameter(parseNetworkOptions,'kmin',[]); 85 | addParameter(parseNetworkOptions,'kmax',[]); 86 | addParameter(parseNetworkOptions,'mu',[]); 87 | addParameter(parseNetworkOptions,'maxreject',[]); 88 | parse(parseNetworkOptions,parseNullOptions.Unmatched); 89 | NetworkOptions=NonDefaultOptions(parseNetworkOptions); 90 | 91 | % generate partition using Dirichlet null distribution 92 | S=PartitionGenerator(nodes,layers,types,dependencyMatrix,... 93 | DirichletNullDistribution(layers,NullOptions),PartitionOptions); 94 | 95 | % generate intralayer edges using DCSBM benchmark model 96 | A=DCSBMNetworkGenerator(S,NetworkOptions); 97 | 98 | end 99 | 100 | function options=NonDefaultOptions(input) 101 | fields=fieldnames(input.Results); 102 | options=struct(); 103 | for i=1:numel(fields) 104 | if ~ismember(fields{i},input.UsingDefaults) 105 | options.(fields{i})=input.Results.(fields{i}); 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /DCSBMNetworkGenerator.m: -------------------------------------------------------------------------------- 1 | function A=DCSBMNetworkGenerator(S,varargin) 2 | % Degree-corrected block-model benchmark 3 | % as suggested in 4 | % Karrer, B., & Newman, M. E. J. (2011). Stochastic blockmodels and 5 | % community structure in networks. Physical Review E, 83(1), 016107. 6 | % http://doi.org/10.1103/PhysRevE.83.016107 7 | % 8 | % Additionally adds rejection of multiedges in the sampling process. 9 | % 10 | % Input: 11 | % 12 | % S: multilayer partition 13 | % 14 | % Options: 15 | % 16 | % parameters for degree_corrected block-model (LFR-like) network 17 | % generation in any format accepted by OptionSturct (i.e., key-value 18 | % list, struct, ...) 19 | % 20 | % exponent: [-3] exponent for the powerlaw degree distribution 21 | % kmin: [3] minimum degree 22 | % kmax: [50] maximum degree 23 | % mu: [0.1] mixing parameter 24 | % maxreject: [100] maximum number of rejections for a single block before 25 | % bailing out and issuing a warning, leaving the block with less than 26 | % the desired number of edges (no multi-edges are generated). 27 | % 28 | % Output: 29 | % 30 | % A: cell array of adjacency matrices for each layer of the sampled 31 | % multilayer network 32 | % 33 | % Version: 2.0.0 34 | % Date: Thu 11 Jul 2019 15:24:15 CEST 35 | % Author: Lucas Jeub 36 | % Email: lucasjeub@gmail.com 37 | % 38 | % References: 39 | % 40 | % [1] Generative benchmark models for mesoscale structure in multilayer 41 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 42 | % Porter. arXiv1:608.06196. 43 | % 44 | % Citation: 45 | % 46 | % If you use this code, please cite as 47 | % Lucas G. S. Jeub and Marya Bazzi 48 | % "A generative model for mesoscale structure in multilayer networks 49 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 50 | 51 | 52 | % parse options 53 | parseArgs=inputParser(); 54 | addParameter(parseArgs,'exponent',-3) 55 | addParameter(parseArgs,'kmin',3) 56 | addParameter(parseArgs,'kmax',50) 57 | addParameter(parseArgs,'mu',.1) 58 | addParameter(parseArgs,'maxreject',100); 59 | parse(parseArgs,varargin{:}); 60 | options=parseArgs.Results; 61 | 62 | mu=options.mu; 63 | exponent=options.exponent; 64 | kmin=options.kmin; 65 | kmax=options.kmax; 66 | max_reject=options.maxreject; 67 | 68 | shape=size(S); 69 | n_nodes=shape(1); 70 | layers=shape(2:end); 71 | 72 | % avoid problems with dimension when we have only one aspect 73 | if length(layers)==1 74 | layers=[layers,1]; 75 | end 76 | 77 | n_layers=prod(layers); 78 | A=cell(layers); 79 | 80 | for layer=1:n_layers 81 | [uc,~,ind]=unique(S(:,ind2sub(layers,layer))); 82 | nc=length(uc); 83 | G=sparse(1:n_nodes,ind,true,n_nodes,nc); 84 | group_sizes=sum(G,1); 85 | k=PowerlawSampler(n_nodes,exponent,kmin,kmax); 86 | k_g=arrayfun(@(i) sum(k(G(:,i))),1:nc); 87 | mm=sum(k); 88 | pos=0; 89 | neighbours=cell(n_nodes,1); 90 | for i=1:n_nodes 91 | neighbours{i}=i; 92 | end 93 | for group1=1:nc 94 | for group2=group1:nc 95 | % compute expected number of edges based on group 96 | % strength and mixing parameter 97 | if group1==group2 98 | w=((1-mu)*k_g(group1)+mu*(k_g(group1).^2)./mm); 99 | else 100 | w=mu*(k_g(group1)*k_g(group2))./mm; 101 | end 102 | % actual number of edges is poisson distributed 103 | m=poissrnd(w); 104 | 105 | if group1==group2 106 | dense=2*m>group_sizes(group1)*(group_sizes(group1)-1); 107 | m=m/2; 108 | else 109 | dense=2*m>group_sizes(group1)*group_sizes(group2); 110 | end 111 | 112 | if dense 113 | % use binomial sampling if block is dense 114 | sigma1=k(G(:,group1))./sum(k(G(:,group1))); 115 | sigma2=k(G(:,group2))./sum(k(G(:,group2))); 116 | P=sigma1*w*sigma2'; 117 | nodes_g1=find(G(:,group1))'; 118 | nodes_g2=find(G(:,group2))'; 119 | ng1=group_sizes(group1); 120 | ng2=group_sizes(group2); 121 | if group1==group2 122 | for it=1:ng1 123 | nid=find(P(it,it+1:end)>rand(1,ng1-it))+it; 124 | neighbours{nodes_g1(it)}=... 125 | [neighbours{nodes_g1(it)},nodes_g2(nid)]; 126 | for it2=1:length(nid) 127 | neighbours{nodes_g2(nid(it2))}=... 128 | [neighbours{nodes_g2(nid(it2))},nodes_g1(it)]; 129 | end 130 | end 131 | else 132 | for it=1:ng1 133 | nid=find(P(it,:)>rand(1,ng2)); 134 | neighbours{nodes_g1(it)}=... 135 | [neighbours{nodes_g1(it)},nodes_g2(nid)]; 136 | for it2=1:length(nid) 137 | neighbours{nodes_g2(nid(it2))}=... 138 | [neighbours{nodes_g2(nid(it2))},nodes_g1(it)]; 139 | end 140 | end 141 | end 142 | 143 | else 144 | % sample the edges 145 | sigma1=cumsum(k(G(:,group1))); 146 | sigma1=sigma1./sigma1(end); 147 | nodes_g1=find(G(:,group1)); 148 | sigma2=cumsum(k(G(:,group2))); 149 | sigma2=sigma2./sigma2(end); 150 | nodes_g2=find(G(:,group2)); 151 | pos=pos+m; 152 | for e=1:m 153 | isneighbour=true; 154 | % rejection sampling to avoid multi-edges and 155 | % self-loops 156 | reject_count=0; 157 | while isneighbour&&reject_count<=max_reject 158 | decide=rand(2,1); 159 | n1=nodes_g1(find(sigma1>decide(1),1)); 160 | n2=nodes_g2(find(sigma2>decide(2),1)); 161 | isneighbour=any(n1==neighbours{n2}(:)); 162 | reject_count=reject_count+1; 163 | end 164 | if reject_count>max_reject 165 | warning('stopping sampling of edges for current block') 166 | break; 167 | end 168 | neighbours{n1}=[neighbours{n1},n2]; 169 | neighbours{n2}=[neighbours{n2},n1]; 170 | end 171 | end 172 | end 173 | end 174 | 175 | % convert neighbour-list to adjacency matrix (note that first 176 | % neighbour is node itself and should not be included in output) 177 | indrow=zeros(2*pos,1); 178 | indcol=zeros(2*pos,1); 179 | pos=0; 180 | for i=1:n_nodes 181 | indrow(pos+1:pos+length(neighbours{i})-1)=i; 182 | indcol(pos+1:pos+length(neighbours{i})-1)=neighbours{i}(2:end); 183 | pos=pos+length(neighbours{i})-1; 184 | end 185 | indrow=indrow(1:pos); 186 | indcol=indcol(1:pos); 187 | A{layer}=sparse(indrow,indcol,1,n_nodes,n_nodes); 188 | end 189 | end 190 | -------------------------------------------------------------------------------- /ConvergenceTest.m: -------------------------------------------------------------------------------- 1 | function ConvergenceTest(nodes,layers,types,dependencyMatrix,nullDistribution,varargin) 2 | % Test convergence of 'PartitionGenerator'. 3 | % 4 | % Input: 5 | % 6 | % nodes: Either a scalar specifying the number of physical nodes or 7 | % a |V_M|x(1+d) matrix specifying each state node in the format 8 | % [node, aspect_1,...,aspect_d] (the latter format allows for missing 9 | % nodes in some layers) 10 | % 11 | % layers: Vector giving the number of elements for each aspect in the 12 | % format [l_1,...,l_d]. Note that for a multilayer network with a 13 | % single aspect this is just a scalar giving the number of layers. 14 | % 15 | % types: 'char' vector specifying the update type for each aspect, 'o' for 16 | % ordered and 'r' for random. 17 | % 18 | % dependencyMatrix: A matrix of copying probabilities, corresponding to 19 | % the flattened interlayer dependency tensor. This matrix is either 20 | % of size lxl for the layer-coupled case or of size (n*l)x(n*l) in 21 | % the general case. If state nodes are given explicitly in 'nodes', 22 | % 'dependencyMatrix' should be of size |V_M|x|V_M|, giving only the 23 | % probabilities for state nodes that are actually present in the 24 | % network. The ways the matrix encodes the tensor are as follows: 25 | % 26 | % layer-coupled case: 27 | % Mapping for flattening the indices: 28 | % 29 | % a=aspect_1+l_1*(aspect_2-1)+...+l_1*l_2*...*l_(d-1)*(aspect_d-1) 30 | % 31 | % where 'dependencyMatrix(a,b)' is the probability that a node in 32 | % layer b copies its community assignment from the same node in 33 | % layer a. The rows of 'dependencyMatrix' should sum to a value<1, 34 | % where 1-sum(transtionMatrix(:,b)) is the probability of 35 | % resampling the community assignment from the specified null 36 | % distribution for a node in layer b. 37 | % 38 | % general case: 39 | % Mapping for flattening the indeces when state nodes are not 40 | % explicitly specified: 41 | % 42 | % i=node+n*(aspect_1-1)+n*l_1*(aspect_2-1)+..._n*l_1*...*l_(d-1)*(aspect_d-1) 43 | % 44 | % When state nodes are given explicitly, the transition matrix 45 | % should have the same number of rows as the matrix of state 46 | % nodes, where the ith state node corresponds to the ith row of 47 | % the transition matrix. 48 | % 49 | % 'dependencyMatrix(i,j)' is the probability that 50 | % state node j copies its community assignment from state node i. 51 | % The rows of 'dependencyMatrix' should sum to a value < 1, 52 | % where 1-sum(transtionMatrix(:,j)) is the probability of 53 | % resampling the community assignment from the specified null 54 | % distribution for node j. 55 | % 56 | % nullDistribution: A function that takes state nodes (i.e., row vectors 57 | % of the form [node, aspect_1,...,aspect_d] ) as input and returns a 58 | % random community assignment. 59 | % 60 | % 61 | % 62 | % 63 | % Options: 64 | % 65 | % Lags: [default: [1,5,10,20]] Lags to plot (note that the unit for the Lags 66 | % depends on the number of 'UpdateSteps' specified) 67 | % 68 | % UpdateSteps: [default: 1] number of Gibbs updates between test partitions 69 | % (i.e., the expected number of times each state node's community 70 | % assignment is updated). 71 | % 72 | % NumberOfSamples: 200 73 | % 74 | % InitialPartition: Optionally specify starting partition (array of 75 | % dimension nxl_1x...xl_d). 76 | % 77 | % 78 | % 79 | % Version: 1.0.1 80 | % Date: Tue 4 Jul 2017 16:38:06 BST 81 | % Author: Lucas Jeub 82 | % Email: ljeub@iu.edu 83 | % 84 | % 85 | % References: 86 | % 87 | % [1] Generative benchmark models for mesoscale structure in multilayer 88 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 89 | % Porter. arXiv1:608.06196. 90 | % 91 | % Citation: 92 | % 93 | % If you use this code, please cite as 94 | % Lucas G. S. Jeub and Marya Bazzi 95 | % "A generative model for mesoscale structure in multilayer networks 96 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 97 | 98 | parseArgs=inputParser(); 99 | addParameter(parseArgs,'InitialPartition',[]); 100 | addParameter(parseArgs,'UpdateSteps',1); 101 | addParameter(parseArgs,'NumberOfSamples',200); 102 | addParameter(parseArgs,'Lags',[1,5,10,20]) 103 | parse(parseArgs,varargin{:}); 104 | options=parseArgs.Results; 105 | options.isset=@(s) ~isempty(options.(s)); 106 | 107 | % determine network shape and set up state nodes if not given explicitly 108 | l=prod(layers); 109 | if numel(nodes)==1 110 | n=nodes; 111 | nodes=ind2subarray([n,layers],1:n*l); 112 | else 113 | n=max(nodes,[],1); 114 | end 115 | 116 | % Sample partitions 117 | S=cell(options.NumberOfSamples+1,1); 118 | if options.isset('InitialPartition') 119 | S{1}=options.InitialPartition; 120 | else 121 | S{1}=zeros([n,layers]); 122 | for i=1:size(nodes,1) 123 | S{1}(subarray2ind([n,layers],nodes(i,:)))=nullDistribution(nodes(i,:)); 124 | end 125 | end 126 | for i=2:length(S) 127 | S{i}=PartitionGenerator(nodes,layers,types,dependencyMatrix,nullDistribution,'InitialPartition',S{i-1},'UpdateSteps',options.UpdateSteps); 128 | end 129 | 130 | for i=1:length(S) 131 | m{i}=nmi_matrix(S{i}); 132 | end 133 | 134 | lag=options.Lags; 135 | lag=lag(lag'}); 139 | sizes=cycler({2,3,3,3,3,3}); 140 | for i=1:length(lag) 141 | for j=lag(i)+1:length(S) 142 | nmi_auto{i}(j-lag(i))=nmi(S{j-lag(i)}(:),S{j}(:)); 143 | d_auto{i}(j-lag(i))=mean(abs(m{j-lag(i)}(triu(true(length(m{j})),1))-m{j}(triu(true(length(m{j})),1)))); 144 | end 145 | end 146 | 147 | f(1)=figure(); 148 | for i=1:length(lag) 149 | plot(nmi_auto{i},'color',map(i,:),'markerfacecolor',map(i,:),... 150 | 'linestyle',styles(i),'marker',markers(i),'markersize',sizes(i)) 151 | hold on 152 | end 153 | legend(arrayfun(@(lag) sprintf('lag=%u',lag),lag,'UniformOutput',false)) 154 | xlabel('step') 155 | ylabel('mNMI') 156 | %set(gca,'xlim',[1,200]) 157 | %set(gca,'xtick',[1,50:50:200]) 158 | ylim=get(gca,'ylim'); 159 | ylim(1)=0; 160 | set(gca,'ylim',ylim); 161 | %set(gca,'box','off','YGrid','on','YMinorGrid','off'); 162 | %plot(nmi_first,'k') 163 | 164 | f(2)=figure(); 165 | for i=1:length(lag) 166 | plot(d_auto{i},'color',map(i,:),'markerfacecolor',map(i,:),... 167 | 'linestyle',styles(i),'marker',markers(i),'markersize',sizes(i)); 168 | hold on 169 | end 170 | xlabel('step') 171 | ylabel('$d$') 172 | %set(gca,'xlim',[1,200]) 173 | ylim=get(gca,'ylim'); 174 | ylim(1)=0; 175 | set(gca,'ylim',ylim); 176 | %set(gca,'xtick',[1,50:50:200]) 177 | %set(gca,'box','off','YGrid','on','YMinorGrid','off'); 178 | %legend(arrayfun(@(lag) sprintf('lag=%u',lag),lag,'UniformOutput',false)) 179 | 180 | %mark_indices=strjoin(arrayfun(@num2str,[1,10:10:200],'uniformoutput',false),','); 181 | end 182 | -------------------------------------------------------------------------------- /PartitionGenerator.m: -------------------------------------------------------------------------------- 1 | function [S]=PartitionGenerator(nodes,layers,types,dependencyMatrix,nullDistribution,varargin) 2 | % Generate partition for a multilayer network 3 | % with specified interlayer dependencies. 4 | % 5 | % Input: 6 | % 7 | % nodes: Either a scalar specifying the number of physical nodes or 8 | % a |V_M|x(1+d) matrix specifying each state node in the format 9 | % [node, aspect_1,...,aspect_d] (the latter format allows for missing 10 | % nodes in some layers) 11 | % 12 | % layers: Vector giving the number of elements for each aspect in the 13 | % format [l_1,...,l_d]. Note that for a multilayer network with a 14 | % single aspect this is just a scalar giving the number of layers. 15 | % 16 | % types: 'char' vector specifying the update type for each aspect, 'o' for 17 | % ordered and 'r' for random. 18 | % 19 | % dependencyMatrix: A matrix of copying probabilities, corresponding to 20 | % the flattened interlayer dependency tensor. This matrix is either 21 | % of size lxl for the layer-coupled case or of size (n*l)x(n*l) in 22 | % the general case. If state nodes are given explicitly in 'nodes', 23 | % 'dependencyMatrix' should be of size |V_M|x|V_M|, giving only the 24 | % probabilities for state nodes that are actually present in the 25 | % network. The ways the matrix encodes the tensor are as follows: 26 | % 27 | % layer-coupled case: 28 | % Mapping for flattening the indices: 29 | % 30 | % a=aspect_1+l_1*(aspect_2-1)+...+l_1*l_2*...*l_(d-1)*(aspect_d-1) 31 | % 32 | % where 'dependencyMatrix(a,b)' is the probability that a node in 33 | % layer b copies its community assignment from the same node in 34 | % layer a. The rows of 'dependencyMatrix' should sum to a value<1, 35 | % where 1-sum(transtionMatrix(:,b)) is the probability of 36 | % resampling the community assignment from the specified null 37 | % distribution for a node in layer b. 38 | % 39 | % general case: 40 | % Mapping for flattening the indeces when state nodes are not 41 | % explicitly specified: 42 | % 43 | % i=node+n*(aspect_1-1)+n*l_1*(aspect_2-1)+..._n*l_1*...*l_(d-1)*(aspect_d-1) 44 | % 45 | % When state nodes are given explicitly, the transition matrix 46 | % should have the same number of rows as the matrix of state 47 | % nodes, where the ith state node corresponds to the ith row of 48 | % the transition matrix. 49 | % 50 | % 'dependencyMatrix(i,j)' is the probability that 51 | % state node j copies its community assignment from state node i. 52 | % The rows of 'dependencyMatrix' should sum to a value < 1, 53 | % where 1-sum(transtionMatrix(:,j)) is the probability of 54 | % resampling the community assignment from the specified null 55 | % distribution for node j. 56 | % 57 | % nullDistribution: A function that takes state nodes (i.e., row vectors 58 | % of the form [node, aspect_1,...,aspect_d] ) as input and returns a 59 | % random community assignment. 60 | % 61 | % 62 | % Output: 63 | % 64 | % S: Multilayer partition after the last update step (returned as an 65 | % array of dimension nxl_1x...xl_d. In the case where state nodes are 66 | % specified directly, missing state nodes have community label 0. 67 | % 68 | % 69 | % Options: 70 | % 71 | % UpdateSteps: [default: 100] number of Gibbs updates before returning 72 | % partition (i.e., the number of times each state node's community 73 | % assignment is updated). This defaults to a single update if the 74 | % provided transition matrix is fully ordered (i.e. has zero 75 | % lower-triangular part. 76 | % 77 | % InitialPartition: Optionally specify starting partition (array of 78 | % dimension nxl_1x...xl_d). 79 | % 80 | % 81 | % 82 | % Version: 2.0.0 83 | % Date: Thu 11 Jul 2019 15:24:15 CEST 84 | % Author: Lucas Jeub 85 | % Email: lucasjeub@gmail.com 86 | % 87 | % 88 | % References: 89 | % 90 | % [1] Generative benchmark models for mesoscale structure in multilayer 91 | % networks, M. Bazzi, L. G. S. Jeub, A. Arenas, S. D. Howison, M. A. 92 | % Porter. arXiv1:608.06196. 93 | % 94 | % Citation: 95 | % 96 | % If you use this code, please cite as 97 | % Lucas G. S. Jeub and Marya Bazzi 98 | % "A generative model for mesoscale structure in multilayer networks 99 | % implemented in MATLAB," https://github.com/MultilayerGM/MultilayerGM-MATLAB (2016-2019). 100 | 101 | 102 | % Parse input 103 | parseArgs=inputParser(); 104 | addParameter(parseArgs,'InitialPartition',[]); 105 | addParameter(parseArgs,'UpdateSteps',100); 106 | parse(parseArgs,varargin{:}); 107 | options=parseArgs.Results; 108 | options.isset=@(s) ~isempty(options.(s)); 109 | 110 | % determine network shape and set up state nodes if not given explicitly 111 | l=prod(layers); 112 | if numel(nodes)==1 113 | n=nodes; 114 | nodes=ind2subarray([n,layers],1:n*l); 115 | else 116 | n=max(nodes,[],1); 117 | end 118 | nvm=size(nodes,1); 119 | 120 | % setup map from state nodes to rows of transition matrix 121 | if isequal(size(dependencyMatrix),[l,l]) 122 | transitionMap=subarray2ind(layers,nodes(:,2:end)); 123 | isLayerCoupled=true; 124 | elseif isequal(size(dependencyMatrix),[nvm,nvm]) 125 | transitionMap=(1:nvm)'; 126 | isLayerCoupled=false; 127 | else 128 | error('CommunityStructureGenerator:dependencyMatrix:size','Specified transition matrix is of inconsistent size'); 129 | end 130 | 131 | % Sample partitions 132 | if options.isset('InitialPartition') 133 | S=options.InitialPartition; 134 | else 135 | S=zeros([n,layers]); 136 | for i=1:size(nodes,1) 137 | S(subarray2ind([n,layers],nodes(i,:)))=nullDistribution(nodes(i,:)); 138 | end 139 | end 140 | 141 | if all(types=='o') 142 | usteps = 1; 143 | else 144 | usteps=options.UpdateSteps; 145 | end 146 | 147 | % set up 148 | shape_ordered = layers(types == 'o'); 149 | if isempty(shape_ordered) 150 | shape_ordered = 1; 151 | end 152 | if numel(shape_ordered) == 1 153 | ordered_layers = cell(shape_ordered, 1); 154 | else 155 | ordered_layers = cell(shape_ordered); 156 | end 157 | 158 | shape_random = layers(types == 'r'); 159 | if isempty(shape_random) 160 | shape_random = 1; 161 | end 162 | if numel(shape_random) == 1 163 | shape_random = [shape_random, shape_random]; 164 | end 165 | for i = 1:numel(ordered_layers) 166 | ordered_layers{i} = cell(shape_random); 167 | end 168 | 169 | for i=1:size(nodes,1) 170 | li = nodes(i, 2:end); 171 | oi = li(types=='o'); 172 | if isempty(oi) 173 | oi = 1; 174 | end 175 | ri = li(types=='r'); 176 | if isempty(ri) 177 | ri = 1; 178 | end 179 | ordered_layers{oi}{ri} = [ordered_layers{oi}{ri}; nodes(i, :)]; 180 | end 181 | 182 | for o=1:numel(ordered_layers) 183 | S=GibbsPartitionSampler(S,ordered_layers{o},transitionMap,dependencyMatrix,isLayerCoupled,nullDistribution,usteps); 184 | end 185 | end 186 | 187 | % Gibbs sampling 188 | function S=GibbsPartitionSampler(S,random_layers,transitionMap,dependencyMatrix,isLayerCoupled,nullDistribution,steps) 189 | % Run Gibbs sampling 190 | % 191 | % Inputs: 192 | % 193 | % S: current multilayer partition 194 | % 195 | % random_layers: cell array of matrices of statenodes (each state node is a row with format 196 | % [node,aspect_1,...,aspect_d]), one for each random layer 197 | % 198 | % transitionMap: vector of indeces mapping statenodes to the 199 | % corresponding row/column of the dependencyMatrix 200 | % 201 | % dependencyMatrix: coupling edges (sum(dependencyMatrix,1)<=1) 202 | % 203 | % isLayerCoupled: bool, true if dependencyMatrix is layer-coupled, false otherwise 204 | % 205 | % nullDistribution: function (statenode->random community assignment) 206 | % 207 | % steps: number of update steps 208 | % 209 | % Output: 210 | % 211 | % S: updated partiton after 'steps' passes over all state nodes 212 | 213 | 214 | not_resample=full(sum(dependencyMatrix,1)); 215 | LW=cumsum(dependencyMatrix,1); 216 | 217 | size_spec=size(S); 218 | 219 | if isLayerCoupled 220 | % layer-coupled 221 | for i=randi(numel(random_layers),[1, steps*numel(random_layers)]) 222 | nodes = random_layers{i}; 223 | for j=1:size(nodes,1) 224 | nodeind=subarray2ind(size_spec,nodes(j,:)); 225 | decide=rand(); 226 | if decidedecide,1))); 228 | else 229 | S(nodeind)=nullDistribution(nodes(j,:)); 230 | end 231 | end 232 | end 233 | else 234 | % general case 235 | for i=1:randi(numel(random_layers),[1, steps*numel(random_layers)]) 236 | nodes = random_layers{i}; 237 | for j=1:size(nodes,1) 238 | nodeind=subarray2ind(size_spec,nodes(j,:)); 239 | decide=rand(); 240 | if decidedecide,1),:))); 242 | else 243 | S(nodeind)=nullDistribution(nodes(j,:)); 244 | end 245 | end 246 | end 247 | end 248 | end 249 | --------------------------------------------------------------------------------