├── GC.m ├── MST.m ├── README.md ├── SPT_forest.m ├── analyze_num_tran.m ├── backbone_tree.m ├── clshead_elect.m ├── cluster_hybridcs.m ├── cluster_hybridcs_distr.m ├── cluster_nocs.m ├── connectivity.m ├── get_loads.m ├── initparams.txt ├── optimal_tree_hybridcs.m ├── plot_cluster.m ├── plot_topology.m ├── simulation.m ├── spt_wo_cs.m └── topology.m /GC.m: -------------------------------------------------------------------------------- 1 | classdef GC 2 | %GLOBALCONST Summary of this class goes here 3 | 4 | properties 5 | end 6 | 7 | methods(Static=true) 8 | 9 | function val = ALG_CLS_HYBCS % clustering with hybrid CS centralized algorithm 10 | val = 1; 11 | end 12 | 13 | function val = ALG_CLS_HYBCS_DISTR % clustering with hybrid CS distributed algorithm 14 | val = 2; 15 | end 16 | 17 | function val = ALG_OPTTREE_CS % optial tree with hybrid CS 18 | val = 3; 19 | end 20 | 21 | function val = ALG_SPT_CS % SPT with hybrid CS 22 | val = 4; 23 | end 24 | 25 | function val = ALG_SPT % SPT without CS 26 | val = 5; 27 | end 28 | 29 | function val = ALG_CLS % clustering without CS 30 | val = 6; 31 | end 32 | 33 | function val = ALG_ANALY % analytical value 34 | val = 7; 35 | end 36 | 37 | function val = ALGTYPE 38 | val = 7; 39 | end 40 | 41 | function val = LOAD_CLS_HYBCS 42 | val = 1; 43 | end 44 | 45 | function val = LOAD_CLS_HYBCS_DISTR 46 | val = 2; 47 | end 48 | 49 | function val = LOAD_OPTTREE_CS 50 | val = 3; 51 | end 52 | 53 | function val = LOADTYPE 54 | val = 3; 55 | end 56 | 57 | function val = ITER_CLS_HYBCS 58 | val = 1; 59 | end 60 | 61 | function val = ITER_CLS_HYBCS_DISTR 62 | val = 2; 63 | end 64 | 65 | function val = ITER_OPTTREE_CS 66 | val = 3; 67 | end 68 | 69 | function val = ITERTYPE 70 | val = 3; 71 | end 72 | 73 | function val = FIGTYPE_HIST 74 | val = 21; 75 | end 76 | 77 | function val = FIGTYPE_CDF 78 | val = 22; 79 | end 80 | end 81 | 82 | end 83 | 84 | -------------------------------------------------------------------------------- /MST.m: -------------------------------------------------------------------------------- 1 | function [cost_mst, leaf_nodes, pred_nodes] = MST(core_nodes, grap_params) 2 | 3 | num_core_nodes = length(core_nodes); 4 | core_adj = zeros(num_core_nodes); 5 | for h = 1:num_core_nodes-1 6 | for g = h+1:num_core_nodes 7 | core_adj(h, g) = grap_params.adj_mtr(core_nodes(h), core_nodes(g)); 8 | end 9 | end 10 | core_adj = core_adj + core_adj'; 11 | 12 | [tree, pred_idx] = graphminspantree(sparse(core_adj)); 13 | [src dst] = find(tree > 0); 14 | 15 | leaf_id = setdiff(src, dst); 16 | leaf_nodes = core_nodes(leaf_id); 17 | 18 | cost_mst = num_core_nodes - 1; 19 | 20 | pred_nodes = [0 core_nodes(pred_idx(2:num_core_nodes))]; 21 | return -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CSCluster 2 | ========= 3 | 4 | Project: Transmission-Efficient Data Collection (2011.5-2012.5) 5 | 6 | Author: Ruitao Xie, City University of Hong Kong 7 | 8 | Objective: collect data from wireless sensor networks with fewest data transmissions using compressive sensing. 9 | 10 | URL: http://www.cs.cityu.edu.hk/~ruitaoxie2/CSCluster.html 11 | 12 | Publication: Ruitao Xie and Xiaohua Jia, Transmission Efficient Clustering Method for Wireless Sensor Networks using Compressive Sensing, IEEE Transactions on Parallel and Distributed Systems (TPDS), 25(3)?806-815, Mar. 2014. 13 | -------------------------------------------------------------------------------- /SPT_forest.m: -------------------------------------------------------------------------------- 1 | function [cost_spt, num_decendants_percore, dst_shell] = SPT_forest(core_nodes, dist) 2 | num_nodes = size(dist,1); 3 | 4 | shell_nodes = 1:num_nodes; 5 | shell_nodes = setdiff(shell_nodes, core_nodes); 6 | dist_shell = zeros(1, length(shell_nodes)); 7 | dst_shell = zeros(1, length(shell_nodes)); 8 | 9 | for k = 1:length(shell_nodes) 10 | this_src = shell_nodes(k); 11 | dist_src = dist(this_src, :); 12 | dist_2core = dist_src(core_nodes); 13 | [dist_shell(k), id] = min(dist_2core); 14 | dst_shell(k) = core_nodes(id); 15 | end 16 | 17 | cost_spt = sum(dist_shell); 18 | num_decendants_percore = zeros(1, length(core_nodes)); 19 | for i = 1:length(core_nodes) 20 | num_decendants_percore(i) = nnz(dst_shell==core_nodes(i)); 21 | end 22 | 23 | return -------------------------------------------------------------------------------- /analyze_num_tran.m: -------------------------------------------------------------------------------- 1 | function [num_tran_opt, num_tran_fn] = analyze_num_tran(initparams) 2 | %{ 3 | @description: compute the analytical value of number of transmissions 4 | by the clustering with hybrid cs 5 | @author: Ruitao Xie, City University of Hong Kong 6 | %} 7 | num_cls = 1:20; 8 | num_nodes_pcls = initparams.N ./ num_cls; 9 | diameter = sqrt(num_nodes_pcls ./ (initparams.lambda * initparams.unit^2)); 10 | cons1 = initparams.N/3 - initparams.M/2; 11 | cons2 = initparams.N * initparams.M / (initparams.lambda * initparams.unit^2) - initparams.N/3; 12 | num_tran_fn = cons1 .* diameter + cons2 ./ diameter; 13 | diameter_opt = sqrt(cons2/cons1); 14 | num_tran_opt = cons1 .* diameter_opt + cons2 ./ diameter_opt; 15 | -------------------------------------------------------------------------------- /backbone_tree.m: -------------------------------------------------------------------------------- 1 | function [pred_c2c, backbone_nodes, dist_backbone, dist_c2c, path_c2c] = backbone_tree(grap_params, cls_params) 2 | %{ 3 | @description: build a backbone routing tree to connect all cluster heads to a sink node 4 | @params: @grap_params, @cls_params 5 | @return: 6 | @pred_c2c: predecessor cluster head of each cluster head 7 | @backbone_nodes: the nodes in the backbone tree 8 | @dist_backbone: distance (i.e., the number of hops) along the backbone tree 9 | @dist_c2c: distance (i.e., the number of hops) from a CH to its predecessor CH 10 | @path_c2c: path from a CH to its predecessor CH 11 | @library func: graphshortestpath, graphminspantree 12 | @author: Ruitao Xie, City University of Hong Kong 13 | %} 14 | 15 | %{ 16 | first build a complete graph, where nodes are cluster heads, and edges between any pair of cluster heads are 17 | their shortest paths; 18 | second compute a minimum spanning tree (MST) on this complete graph; 19 | third build a backbone tree from the MST 20 | %} 21 | 22 | CH = cls_params.head(:); 23 | num_cls = length(CH); 24 | core_nodes = [1 CH']; 25 | num_CN = length(core_nodes); % the sink node is included 26 | dist_CN2CN_all = inf(num_CN, num_CN); 27 | path_CN2CN_all = cell(num_CN, num_CN); 28 | path_c2c = cell(1, num_cls); 29 | dist_c2c = zeros(1, num_cls); 30 | backbone_nodes = []; 31 | 32 | % build a complete graph 33 | for i_cn = 1 : num_CN-1 34 | for j_cn = i_cn + 1 : num_CN 35 | [dist_CN2CN_all(i_cn, j_cn) path_CN2CN_all{i_cn, j_cn}] = graphshortestpath(grap_params.adj_mtr, core_nodes(i_cn), core_nodes(j_cn), 'directed', false); 36 | end 37 | end 38 | adj_CN2CN = triu(dist_CN2CN_all, 1) + triu(dist_CN2CN_all, 1)'; 39 | 40 | % compute a mst 41 | [~, pred] = graphminspantree(sparse(adj_CN2CN), 1); 42 | pred_c2c = core_nodes(pred(2:num_CN)); 43 | 44 | % build a backbone tree from the mst 45 | for i_cn = 2 : num_CN 46 | if i_cn < pred(i_cn) 47 | this_path = path_CN2CN_all{i_cn, pred(i_cn)}; 48 | else 49 | this_path = path_CN2CN_all{pred(i_cn), i_cn}; 50 | end 51 | path_c2c{i_cn-1} = this_path; 52 | dist_c2c(i_cn-1) = length(this_path) - 1; 53 | assert( dist_c2c(i_cn-1) == adj_CN2CN(i_cn, pred(i_cn)) ); 54 | backbone_nodes = [backbone_nodes this_path]; 55 | end 56 | backbone_nodes = unique(backbone_nodes); 57 | dist_backbone = length(backbone_nodes)-1; 58 | assert(dist_backbone <= sum(dist_c2c), 'something wrong in calculation of dist between CHs!'); 59 | -------------------------------------------------------------------------------- /clshead_elect.m: -------------------------------------------------------------------------------- 1 | function cls_params = clshead_elect(initparams, grap_params, cls_params) 2 | %{ 3 | @description: Given the geographic location of the central point of a 4 | cluster-area, the sensor node that is the closest to the central 5 | point will become the CH. 6 | @author: Ruitao Xie, City University of Hong Kong 7 | %} 8 | [i_max j_max] = size(cls_params.locx); 9 | head = zeros(i_max, j_max); 10 | 11 | for i = 1:i_max 12 | for j = 1: j_max 13 | [~, id] = min((grap_params.locationx - cls_params.locx(i, j)).^2 + (grap_params.locationy - cls_params.locy(i, j)).^2); 14 | head(i, j) = id; 15 | end 16 | end 17 | cls_params.head = head; 18 | -------------------------------------------------------------------------------- /cluster_hybridcs.m: -------------------------------------------------------------------------------- 1 | function [num_tran, loads, num_iter, cls_params] = cluster_hybridcs(initparams, grap_params, sp_dist, num_cls) 2 | %{ 3 | @description: clustering with hybrid CS centralized algorithm 4 | @params: 5 | @initparams: the initialization parameters 6 | @grap_params: the parameters of graph 7 | @sp_dist: the distance (i.e., the number of hops) between each pair of nodes 8 | @num_cls: the number of clusters required 9 | @return: 10 | @num_tran: the number of transmissions in total 11 | @loads: the number of transmissions of each node, including intra-cluster transmission and inter-cluster transmission 12 | @num_iter: the number of iterations to compute cluster heads 13 | @cls_params: information on cluster heads 14 | @required: backbone_tree.m, get_loads.m 15 | @author: Ruitao Xie, City University of Hong Kong 16 | %} 17 | 18 | %{ 19 | first generate @num_cls cluster heads by k-median clustering algorithm 20 | second connect nodes to cluster heads by shortest paths 21 | third build a minumum steiner tree to connect all cluster heads to sink node 22 | %} 23 | 24 | % first iteratively generate @num_cls cluster heads 25 | idx_2cent = zeros(1, initparams.N+1); 26 | dist_2cent = zeros(1, initparams.N+1); 27 | nodes_pcls = cell(1, num_cls); 28 | counts_pcls = zeros(1, num_cls); 29 | dist_sum_2clsnodes = zeros(1, initparams.N+1); 30 | 31 | num_iter = 0; % the number of iterations 32 | % initialize a set of centers 33 | while 1 34 | new_centers = randi([1, initparams.N], 1, num_cls) + 1; % the id of sink node is 1 35 | if length(unique(new_centers)) == num_cls 36 | break; 37 | end 38 | end 39 | 40 | while 1 41 | num_iter = num_iter + 1; 42 | %fprintf(1,'%d th iteration\n', num_iter); 43 | centers = new_centers; 44 | 45 | % connect nodes to closest centers 46 | for i_node = 1:initparams.N+1 47 | this_dist = sp_dist(i_node, centers); 48 | [dist_2cent(i_node), idx_2cent(i_node)] = min(this_dist); 49 | end 50 | for i_cls = 1:num_cls 51 | nodes_pcls{i_cls} = find(idx_2cent == i_cls); 52 | counts_pcls(i_cls) = length(nodes_pcls{i_cls}); 53 | end 54 | assert(sum(counts_pcls) == initparams.N+1, 'something wrong in finding the members of each cluster!'); 55 | 56 | % generate a new center for each cluster 57 | for i_cls = 1:num_cls 58 | this_nodes_pcls = nodes_pcls{i_cls}; 59 | for i_clsnode = 1:counts_pcls(i_cls) 60 | this_node = this_nodes_pcls(i_clsnode); 61 | dist_sum_2clsnodes(this_node) = sum(sp_dist(this_node, this_nodes_pcls)); 62 | end 63 | [~, id] = min(dist_sum_2clsnodes(this_nodes_pcls)); 64 | new_centers(i_cls) = this_nodes_pcls(id); 65 | end 66 | 67 | % converge or not 68 | if (nnz(centers == new_centers) == num_cls) 69 | break; 70 | end 71 | end 72 | 73 | cls_params.head = centers; 74 | 75 | % build a minumum steiner tree to connect all cluster heads to sink node 76 | [pred_c2c, backbone_nodes, dist_backbone, ~, path_c2c] = backbone_tree(grap_params, cls_params); 77 | 78 | % get loads 79 | [dist_n2c, idx_n2c, loads, pred_n2c] = get_loads(initparams, grap_params, cls_params, backbone_nodes); 80 | 81 | % compute the number of transmissions in total 82 | num_tran = dist_backbone * initparams.M + sum(dist_n2c) - dist_n2c(1); 83 | 84 | % plot clustering result 85 | if initparams.fig 86 | plot_cluster(initparams, grap_params, cls_params, idx_n2c, pred_n2c, path_c2c); 87 | end -------------------------------------------------------------------------------- /cluster_hybridcs_distr.m: -------------------------------------------------------------------------------- 1 | function [num_tran, cls_params, dist_n2c, idx_n2c] = cluster_hybridcs_distr(initparams, grap_params, num_cls) 2 | %{ 3 | @description: clustering with hybrid CS distributed algorithm 4 | @params: 5 | @initparams: the initialization parameters 6 | @grap_params: the parameters of graph 7 | @num_cls: the number of clusters required 8 | @return: 9 | @num_tran: the number of transmissions 10 | @cls_params: information on cluster heads 11 | @dist_n2c: the distance (the number of hops) from each node to its closest cluster head 12 | @idx_n2c: the index of the cluster head that each node connects to 13 | @required: clshead_elect.m, backbone_tree.m, get_loads.m 14 | @author: Ruitao Xie, City University of Hong Kong 15 | %} 16 | 17 | %{ 18 | first compute the geographic location of the central point of each cluster-area; 19 | second select the sensor node that is the closest to the central point as a CH; 20 | third compute a backbone tree that connect all CHs to the sink node. 21 | %} 22 | 23 | % compute the geographic location of the central point of each cluster-area 24 | xloc = 1:initparams.length; 25 | yloc = 1:initparams.width; 26 | locs = combvec(xloc, yloc)'; 27 | [~, ctrs] = kmeans(locs, num_cls); 28 | cls_params.locx = ctrs(:,1); 29 | cls_params.locy = ctrs(:,2); 30 | 31 | % select the sensor node that is the closest to the central point as a CH 32 | [cls_params] = clshead_elect(initparams, grap_params, cls_params); 33 | 34 | % compute a backbone tree that connect all CHs to the sink node 35 | [~, backbone_nodes, dist_backbone] = backbone_tree(grap_params, cls_params); 36 | 37 | % compute loads and number of transmissions 38 | [dist_n2c, idx_n2c, loads] = get_loads(initparams, grap_params, cls_params, backbone_nodes); 39 | num_tran = dist_backbone * initparams.M + sum(dist_n2c) - dist_n2c(1); 40 | assert(sum(loads) == num_tran); 41 | -------------------------------------------------------------------------------- /cluster_nocs.m: -------------------------------------------------------------------------------- 1 | function num_tran = cluster_nocs(initparams, grap_params, cls_params, dist_n2c, idx_n2c) 2 | %{ 3 | @description: clustering without CS 4 | @params: 5 | @initparams: the initialization parameters 6 | @grap_params: the parameters of graph 7 | @cls_params: information on cluster heads 8 | @dist_n2c: the distance (the number of hops) from each node to its closest cluster head 9 | @idx_n2c: the index of the cluster head that each node connects to 10 | @return: 11 | @num_tran: the number of transmissions 12 | @author: Ruitao Xie, City University of Hong Kong 13 | %} 14 | 15 | [dist_2sink, ~, ~] = graphshortestpath(grap_params.adj_mtr, 1, 'directed', false); 16 | [i_max j_max] = size(cls_params.head); 17 | 18 | num_nodes_percls = zeros(1, i_max*j_max); 19 | for i = 1: i_max 20 | for j = 1:j_max 21 | id = sub2ind([i_max j_max], i, j); 22 | num_nodes_percls(id) = nnz(idx_n2c == id); % the cluster head itself is counted 23 | end 24 | end 25 | assert(sum(num_nodes_percls) == initparams.N+1, 'something wrong in calculation of the number of nodes per cluser!'); 26 | dist_CH2sink = dist_2sink(cls_params.head(:)); 27 | 28 | %{ 29 | the first term is for transmitting the data of all nodes from cluster heads to the sink; 30 | the second term is for transmitting the data of member nodes from member nodes to the cluster heads. 31 | %} 32 | 33 | num_tran = dist_CH2sink * num_nodes_percls' + sum(dist_n2c) - dist_n2c(1); 34 | 35 | -------------------------------------------------------------------------------- /connectivity.m: -------------------------------------------------------------------------------- 1 | function [con unconnodes] = connectivity(costMatrix) 2 | [n,n]=size(costMatrix); 3 | 4 | for i = 1:n 5 | for j = 1:n 6 | if costMatrix(i,j) == 0 7 | costMatrix(i,j) = inf; 8 | end 9 | end 10 | end 11 | 12 | % all the nodes are un-visited; 13 | visited(1:n) = 0; 14 | 15 | distance(1:n) = inf; % it stores the shortest distance between each node and the source node; 16 | parent(1:n) = 0; 17 | 18 | distance(1) = 0; 19 | for h = 1:(n-1) 20 | temp = []; 21 | for j = 1:n 22 | if visited(j) == 0 % in the tree; 23 | temp = [temp distance(j)]; 24 | else 25 | temp = [temp inf]; 26 | end 27 | end 28 | [t, u] = min(temp); % it starts from node with the shortest distance to the source; 29 | 30 | visited(u) = 1; % mark it as visited; 31 | for v = 1:n % for each neighbors of node u; 32 | if ( ( costMatrix(u, v) + distance(u)) < distance(v) ) 33 | distance(v) = distance(u) + costMatrix(u, v); % update the shortest distance when a shorter path is found; 34 | parent(v) = u; % update its parent; 35 | end 36 | end 37 | end 38 | 39 | parent(1) = 1; 40 | unconnodes = []; 41 | for i = 2:n 42 | if parent(i) == 0 43 | unconnodes = [unconnodes i]; 44 | end 45 | end 46 | if min(parent) == 0 47 | con = 0; 48 | else 49 | con = 1; 50 | end 51 | 52 | return -------------------------------------------------------------------------------- /get_loads.m: -------------------------------------------------------------------------------- 1 | function [dist_n2c, idx_n2c, loads, pred_n2c] = get_loads(initparams, grap_params, cls_params, backbone_nodes) 2 | %{ 3 | @description: compute the number of transmissions of each node 4 | @params: 5 | @initparams, @grap_params, @cls_params, 6 | @backbone_nodes: the nodes in the backbone tree 7 | @return: 8 | @dist_n2c: the distance from each node to the nearest CH 9 | @idx_n2c: the index of nearest CH (in 1...num_cls) to which each node connects 10 | @loads: the number of transmissions of each node, including intra-cluster transmission and inter-cluster transmission 11 | @pred_n2c: the predecessor node to which each node connects in its cluster 12 | @required: NA 13 | @author: Ruitao Xie, City University of Hong Kong 14 | %} 15 | 16 | %{ 17 | first compute load in intra-cluster transmission, that is the number of descendant 18 | nodes of each node (including itself) in the intracluster tree; 19 | second add the inter-cluster transmission into load. 20 | %} 21 | CH = cls_params.head(:); 22 | num_cls = length(CH); 23 | dist_2allCHs = zeros(num_cls, initparams.N+1); 24 | pred_2allCHs = zeros(num_cls, initparams.N+1); 25 | pred_n2c = zeros(1, initparams.N+1); 26 | num_desc_nodes = zeros(1, initparams.N+1); 27 | intra_tree = zeros(initparams.N+1); 28 | 29 | for i_cls = 1:num_cls 30 | [dist_2allCHs(i_cls,:), ~, pred_2allCHs(i_cls,:)] = graphshortestpath(grap_params.adj_mtr, CH(i_cls), 'directed', false); 31 | end 32 | 33 | [dist_n2c, idx_n2c] = min(dist_2allCHs, [], 1); 34 | 35 | for i_node = 1:initparams.N+1 36 | pred_n2c(i_node) = pred_2allCHs(idx_n2c(i_node), i_node); 37 | if pred_n2c(i_node) ~= 0 38 | intra_tree(pred_n2c(i_node), i_node) = 1; 39 | end 40 | end 41 | bio_intra_tree = biograph(intra_tree); 42 | set(bio_intra_tree.nodes(CH),'Color',[1 0.4 0.4]); 43 | if initparams.showtree 44 | bio_intra_tree.view; 45 | end 46 | 47 | for i = 1:initparams.N+1 48 | num_generations = 1; 49 | while 1 50 | temp = length(getdescendants(bio_intra_tree.nodes(i), num_generations)); 51 | if temp == num_desc_nodes(i) 52 | break; 53 | else 54 | num_desc_nodes(i) = temp; 55 | num_generations = num_generations + 1; 56 | end 57 | end 58 | end 59 | 60 | assert(sum(num_desc_nodes) - sum(num_desc_nodes(CH)) == sum(dist_n2c)); 61 | assert(sum(num_desc_nodes(CH)) == initparams.N+1); 62 | 63 | loads = num_desc_nodes; 64 | % the sink node has no data to transmit, so it incurrs no load for the 65 | % predecessor nodes 66 | this_pred = 1; 67 | while 1 68 | if this_pred == CH(idx_n2c(1)) 69 | break; 70 | else 71 | loads(this_pred) = loads(this_pred) - 1; 72 | this_pred = pred_n2c(this_pred); 73 | end 74 | end 75 | loads(CH) = 0; 76 | assert( sum(loads) == sum(dist_n2c) - dist_n2c(1)); 77 | loads(backbone_nodes) = loads(backbone_nodes) + initparams.M; 78 | loads(1) = 0; % 1 for sink node -------------------------------------------------------------------------------- /initparams.txt: -------------------------------------------------------------------------------- 1 | %{ 2 | @description of initparams 3 | @initparams.fig: whether show figure 4 | @initparams.showtree: whether show tree topology 5 | @initparams.length: the length of a rectangle field 6 | @initparams.width: the width of a rectangle field 7 | @initparams.N: the number of nodes 8 | @initparams.rho: the compressive ratio of compressive sensing (# of original data / # of projections required) 9 | @initparams.unit: the unit of rectangle field 10 | @initparams.range = initparams.unit * sqrt(2): the transmission range of two nodes 11 | @initparams.M = round(initparams.N / initparams.rho): the number of projections required by compressive sensing 12 | @initparams.lambda = initparams.N / (initparams.len * initparams.wide): the number of nodes distributed in a area of 1 unit square 13 | @initparams.num_sim = 50: the number of rounds to simulate 14 | %} -------------------------------------------------------------------------------- /optimal_tree_hybridcs.m: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dorisxinyi/CSCluster/665105a9238883c9904b3f2307bf8e1ced3dd375/optimal_tree_hybridcs.m -------------------------------------------------------------------------------- /plot_cluster.m: -------------------------------------------------------------------------------- 1 | function plot_cluster(initparams, grap_params, cls_params, idx_n2c, pred_n2c, path_c2c) 2 | %{ 3 | @description: plot clustering results 4 | @params: 5 | @initparams: initialization parameters 6 | @grap_params: parameters of graph 7 | @cls_params: information on cluster 8 | @idx_n2c: the index of nearest CH (in 1...num_cls) to which each node connects 9 | @pred_n2c: the predecessor node to which each node connects in its cluster 10 | @path_CH: path from a CH to its predecessor CH 11 | @author: Ruitao Xie, City University of Hong Kong 12 | %} 13 | 14 | heads = cls_params.head; 15 | num_cls = length(heads); 16 | locx = grap_params.locationx; 17 | locy = grap_params.locationy; 18 | L = initparams.length; 19 | W = initparams.width; 20 | 21 | figure 22 | hold on 23 | axis([0 L 0 W]); 24 | axis off 25 | axis equal; 26 | set(gcf, 'color', 'w'); 27 | colorchar = 'rgbmk'; 28 | 29 | % plot a frame 30 | line([0 L], [W W], 'color', 'k', 'lineWidth', 1); 31 | line([L L], [0 W], 'color', 'k', 'lineWidth', 1); 32 | line([0 0], [0 W], 'color', 'k', 'lineWidth', 1); 33 | line([0 L], [0 0], 'color', 'k', 'lineWidth', 1); 34 | 35 | % plot nodes 36 | for i_cls = 1:num_cls 37 | linespec = colorchar( mod(i_cls,length(colorchar))+1 ); 38 | plot(locx(idx_n2c == i_cls), locy(idx_n2c == i_cls), ... 39 | 'o', 'MarkerEdgeColor', linespec,... 40 | 'MarkerFaceColor', linespec, 'MarkerSize',4); 41 | CH_str = ['CH' int2str(i_cls)]; 42 | text(locx(heads(i_cls))+0.2,locy(heads(i_cls))+0.2, CH_str, 'FontSize',14, 'FontWeight', 'bold'); 43 | end 44 | plot(locx(heads),locy(heads),'s', 'MarkerEdgeColor','b', 'MarkerFaceColor', 'b', 'MarkerSize', 12); 45 | plot(0, 0, '^', 'MarkerEdgeColor','b', 'MarkerFaceColor', 'b', 'Markersize', 12); 46 | text(0.2, -0.1, 'Sink', 'FontSize',14,'FontWeight', 'bold'); 47 | 48 | % plot the shortest path within cluster 49 | for i_node = 2:initparams.N+1 50 | if pred_n2c(i_node) ~= 0 % otherwise node i_node is a cluster head 51 | line([locx(i_node) locx(pred_n2c(i_node))], ... 52 | [locy(i_node) locy(pred_n2c(i_node))], 'color', 'r', 'linewidth', 2); 53 | end 54 | end 55 | 56 | % plot the backbone routing tree 57 | for i_cls = 1:num_cls 58 | this_path = path_c2c{i_cls}; 59 | for n = 1:length(this_path)-1 60 | line([locx(this_path(n)) locx(this_path(n+1))], ... 61 | [locy(this_path(n)) locy(this_path(n+1))], 'color', 'b', 'linewidth', 3); 62 | end 63 | end 64 | 65 | -------------------------------------------------------------------------------- /plot_topology.m: -------------------------------------------------------------------------------- 1 | function plot_topology(initparams, grap_params) 2 | %{ 3 | @description: plot topology according to @initparams and @grap_params 4 | @required: NA 5 | @author: Ruitao Xie City University of Hong Kong 6 | %} 7 | 8 | N = initparams.N; 9 | L = initparams.length; 10 | W = initparams.width; 11 | locationx = grap_params.locationx; 12 | locationy = grap_params.locationy; 13 | neighbor = grap_params.neighbor; 14 | 15 | figure(1) 16 | hold on 17 | grid off 18 | axis([0 L 0 W]); 19 | axis off 20 | axis equal; 21 | set(gcf, 'color', 'w'); 22 | 23 | % plot a frame 24 | line([0 L], [W W], 'color', 'k', 'lineWidth', 1); 25 | line([L L], [0 W], 'color', 'k', 'lineWidth', 1); 26 | line([0 0], [0 W], 'color', 'k', 'lineWidth', 1); 27 | line([0 L], [0 0], 'color', 'k', 'lineWidth', 1); 28 | 29 | % plot nodes 30 | plot(locationx, locationy, 'o', 'MarkerEdgeColor','b', 'MarkerFaceColor', 'b', 'Markersize', 3); 31 | plot(0, 0, '^', 'MarkerEdgeColor','b', 'MarkerFaceColor', 'b', 'Markersize', 10); 32 | text(0.2, -0.3, 'sink'); 33 | 34 | % plot edges 35 | for i = 1:N+1 36 | neigh_temp = neighbor{i}; 37 | num = length(neigh_temp); 38 | for k = 1:num 39 | j = neigh_temp(k); 40 | line([locationx(i) locationx(j)], [locationy(i) locationy(j)]); 41 | end 42 | end -------------------------------------------------------------------------------- /simulation.m: -------------------------------------------------------------------------------- 1 | %{ 2 | @description: a simulation script 3 | @required: topology.m, cluster_hybridcs.m, cluster_hybridcs_distr.m, 4 | cluster_nocs.m, optimal_tree_hybridcs.m, spt_wo_cs.m 5 | @author: Ruitao Xie City University of Hong Kong 6 | %} 7 | 8 | %% 9 | fileuid = randi(1e7,1); % a random number to generate a filename 10 | scales = 4:2:4; % the number of nodes = scales * 100 11 | num_scales = length(scales); 12 | num_tran_mean = zeros(GC.ALGTYPE, num_scales); % the number of transmissions averaged over multiple simulation rounds 13 | num_iter_mean = zeros(GC.ITERTYPE, num_scales); % the number of iterations to compute cluster heads averaged over multiple simulation rounds 14 | CI_lower = zeros(num_scales, GC.ALGTYPE); % the confidence interval of the number of transmissions over multiple simulation rounds 15 | CI_upper = zeros(num_scales, GC.ALGTYPE); 16 | iter_CI_lower = zeros(num_scales, GC.ITERTYPE); % the confidence interval of the number of iterations over multiple simulation rounds 17 | iter_CI_upper = zeros(num_scales, GC.ITERTYPE); 18 | 19 | %% simulation in various scales of networks 20 | for i_scale = 1:num_scales 21 | % initialize simulation parameters, the meanings of parameters refer to initparams.txt 22 | initparams.fig = 1; 23 | initparams.showtree = 0; 24 | initparams.length = 20; 25 | initparams.width = 10; 26 | initparams.N = scales(i_scale) * 100; 27 | initparams.rho = 10; 28 | initparams.unit = 1; 29 | initparams.range = initparams.unit * sqrt(2); 30 | initparams.M = round(initparams.N / initparams.rho); 31 | initparams.lambda = initparams.N / (initparams.length * initparams.width); 32 | initparams.num_sim = 1; 33 | 34 | % initialize variables to store results 35 | num_tran = zeros(GC.ALGTYPE, initparams.num_sim); 36 | num_tran_2cls = zeros(20, initparams.num_sim); 37 | num_iter = zeros(GC.ITERTYPE, initparams.num_sim); 38 | loads = cell(GC.LOADTYPE, initparams.num_sim); 39 | 40 | for i_sim = 1:initparams.num_sim 41 | fprintf(1,'%d th instance\n',i_sim); 42 | grap_params = topology(initparams); 43 | sp_dist = graphallshortestpaths(grap_params.adj_mtr); 44 | 45 | % compute the optimal number of clusters by analytical model 46 | num_nodes_pcls = (3*initparams.M - initparams.lambda)/(1-3/(2*initparams.rho)); 47 | num_cls_opt = round(initparams.N/num_nodes_pcls); 48 | 49 | % simulate various methods 50 | % clustering with hybrid CS centralized algorithm 51 | [num_tran(GC.ALG_CLS_HYBCS, i_sim), loads{GC.LOAD_CLS_HYBCS, i_sim}, num_iter(GC.ITER_CLS_HYBCS, i_sim), cls_params] = cluster_hybridcs(initparams, grap_params, sp_dist, num_cls_opt); 52 | 53 | % clustering with hybrid CS distributed algorithm 54 | [num_tran(GC.ALG_CLS_HYBCS_DISTR, i_sim), ~, dist_n2c, idx_n2c] = cluster_hybridcs_distr(initparams, grap_params, num_cls_opt); 55 | 56 | % clustering without CS 57 | num_tran(GC.ALG_CLS, i_sim) = cluster_nocs(initparams, grap_params, cls_params, dist_n2c, idx_n2c); 58 | 59 | % optimal tree with hybrid CS 60 | [num_tran(GC.ALG_OPTTREE_CS, i_sim), loads{GC.LOAD_OPTTREE_CS, i_sim}, num_iter(GC.ITER_OPTTREE_CS, i_sim)] = optimal_tree_hybridcs(grap_params, initparams, sp_dist); 61 | 62 | % SPT without CS and SPT with hybrid CS 63 | [num_tran(GC.ALG_SPT, i_sim), num_tran(GC.ALG_SPT_CS, i_sim)] = spt_wo_cs(initparams, grap_params, sp_dist); 64 | 65 | end 66 | % postprocessing 1: compute mean of the number of transmissions as well as the analytical value of number of transmissions 67 | num_tran_mean(:, i_scale) = mean(num_tran, 2); 68 | [num_tran_opt, num_tran_fn] = analyze_num_tran(initparams); 69 | num_tran_mean(GC.ALG_ANALY, i_scale) = num_tran_opt; 70 | 71 | % postprocessing 2: compute the confidence interval 72 | [~,~,muci,~] = normfit(num_tran'); 73 | CI_lower(i_scale, :) = muci(1, :); 74 | CI_upper(i_scale, :) = muci(2, :); 75 | 76 | % postprocessing 3: compute mean and confidence interval of the number of iterations 77 | num_iter_mean(:, i_scale) = mean(num_iter, 2); 78 | [muhat,~,muci,~] = normfit(num_iter'); 79 | iter_CI_lower(i_scale, :) = muci(1, :); 80 | iter_CI_upper(i_scale, :) = muci(2, :); 81 | 82 | % postprocessing 4: compute the reduction ratio of the number of transmissions 83 | num_temp = num_tran_mean - repmat(num_tran_mean(1,:),size(num_tran_mean,1),1); 84 | num_tran_ratio = num_temp./num_tran_mean * 100; 85 | 86 | % postprocessing 5: write the results in a file 87 | filename = sprintf('%d_len%d_wide%d_RHO%d_N%d', fileuid, initparams.length, initparams.width,... 88 | initparams.rho, initparams.N); 89 | save(filename); 90 | end -------------------------------------------------------------------------------- /spt_wo_cs.m: -------------------------------------------------------------------------------- 1 | function [num_tran_spt, num_tran_sptcs] = spt_wo_cs(initparams, grap_params, sp_dist) 2 | %{ 3 | @description: SPT without CS and SPT with hybrid CS 4 | @params: 5 | @initparams, @grap_params, @sp_dist 6 | @return: 7 | @num_tran_spt: the number of transmissions of SPT without CS 8 | @num_tran_sptcs: the number of transmissions of SPT with hybrid CS 9 | @author: Ruitao Xie, City University of Hong Kong 10 | %} 11 | 12 | % SPT without CS 13 | num_tran_spt = sum(sp_dist(:,1)); 14 | [~, ~, pred] = graphshortestpath(grap_params.adj_mtr, 1, 'directed', false); 15 | spt_tree = zeros(initparams.N+1); 16 | for i = 2:initparams.N+1 17 | spt_tree(pred(i), i) = 1; 18 | end 19 | bio_spt_tree = biograph(spt_tree); 20 | if initparams.showtree 21 | bio_spt_tree.view; 22 | end 23 | 24 | % SPT with hybrid CS 25 | num_desc_nodes = zeros(1, initparams.N+1); % num of descendants: the node itself is included 26 | for i = 1:initparams.N+1 27 | num_generations = 1; 28 | while 1 29 | temp = length(getdescendants(bio_spt_tree.nodes(i), num_generations)); 30 | if temp == num_desc_nodes(i) 31 | break; 32 | else 33 | num_desc_nodes(i) = temp; 34 | num_generations = num_generations + 1; 35 | end 36 | end 37 | end 38 | num_desc_nodes(1) = 0; 39 | assert(sum(num_desc_nodes) == num_tran_spt); 40 | num_tran_nodes = num_desc_nodes; 41 | num_tran_nodes(num_desc_nodes >= initparams.M) = initparams.M; 42 | 43 | num_tran_sptcs = sum(num_tran_nodes); 44 | if initparams.showtree 45 | set(bio_spt_tree.nodes(num_desc_nodes >= initparams.M),'Color',[1 0.4 0.4]); 46 | view(bio_spt_tree); 47 | end 48 | -------------------------------------------------------------------------------- /topology.m: -------------------------------------------------------------------------------- 1 | function grap_params = topology(initparams) 2 | %{ 3 | @description: generate topology according to @initparams 4 | @return: @grap_params 5 | @required: connectivity.m plot_topology.m 6 | @author: Ruitao Xie City University of Hong Kong 7 | %} 8 | %{ 9 | The topology consists of N sensor nodes and a sink node which locates 10 | at the corner of the field, so there are N+1 nodes in total. 11 | The variable @adj_mtr is N+1 by N+1. 12 | %} 13 | 14 | N = initparams.N; 15 | L = initparams.length; 16 | W = initparams.width; 17 | r = initparams.range; 18 | 19 | unconnect = 1; 20 | while unconnect 21 | adj_mtr = zeros(N+1); 22 | 23 | % generate randomly according to the Uniform distribution 24 | locationx = [0 rand(1, N)] * L; 25 | locationy = [0 rand(1, N)] * W; 26 | locations = [locationx; locationy]'; 27 | dist_mtr = squareform( pdist(locations,'euclidean') ); 28 | adj_mtr(dist_mtr > 0 & dist_mtr <= r) = 1; 29 | 30 | % check the connectivity of the graph 31 | connect = connectivity(adj_mtr); 32 | if connect 33 | unconnect = 0; 34 | end 35 | end 36 | 37 | % compute adjacent list 38 | neighbor = cell(N+1, 1); 39 | for i = 1:N+1 40 | neighbor{i} = find(adj_mtr(i,:)); 41 | end 42 | 43 | grap_params.adj_mtr = sparse(adj_mtr); 44 | grap_params.neighbor = neighbor; 45 | grap_params.locationx = locationx; 46 | grap_params.locationy = locationy; 47 | grap_params.num_nodes = N+1; 48 | 49 | % plot topology 50 | if initparams.fig 51 | plot_topology(initparams, grap_params); 52 | end 53 | 54 | %{ 55 | % read in from a file 56 | flocid = fopen('location.txt', 'r'); 57 | locationx = fscanf(flocid, '%g %g', [1 N+1]); 58 | locationy = fscanf(flocid, '%g %g', [1 N+1]); 59 | fclose(flocid); 60 | %} 61 | %{ 62 | % write to a file 63 | flocid = fopen('location.txt', 'w'); 64 | fprintf(flocid, '%.2f\t', locationx); 65 | fprintf(flocid, '\n'); 66 | fprintf(flocid, '%.2f\t', locationy); 67 | fclose(flocid); 68 | %} 69 | --------------------------------------------------------------------------------