A simple algorithm
The beauty of core-numbers is that they arey very intuitive to compute and understand.
Let's load some data. This graph comes from the paper with the O(m) algorithm to compute the core numbers of an undirected
graph by Batagelj and Zaversnik, "An O(m) algorithm for the cores decomposition of a network."
load '../graphs/cores_example.mat';
Plot the data.
gplot(A,xy); hold on; plot(xy(:,1), xy(:,2),'r.','MarkerSize',24); hold off;
text(xy(:,1)+0.1, xy(:,2)+0.1, num2str((1:21)'));set(gcf,'Color',[1,1,1]);
set(gca,'XTick',[]);set(gca,'YTick',[]);xlim([-1,10]);ylim([-2,7]);axis off;
By inspection, vertex 16 is in a 0 core because it has no edges in the graph.
From the statement of the property, let's figure out the core numbers for this graph. For each possible degree d, let's remove
all vertices with degree <= d and degree>0 and repeat this until there are no vertices with degree (0,d]. Then, any vertex
that is left, must have core number at least d+1.
The following code implements that algorithm where the graph Ad is the current working version of the graph. At the end of
the for loop, the graph Ad is the graph A where all vertices of degree <= d have been removed.
max_deg = full(max(sum(A)));
cn = zeros(num_vertices(A),1);
Ad = A; dv=sum(Ad);
for d=0:max_deg
while any(dv<=d&dv>0), Ad(dv<=d,:)=0;Ad(:,dv<=d)=0;dv=sum(Ad);end
cn(dv>d) = d+1;
end
arrayfun(@(v,c) fprintf('core_number(%2i) = %i\n',v,c),1:size(A,1),cn');
core_number( 1) = 1
core_number( 2) = 2
core_number( 3) = 2
core_number( 4) = 3
core_number( 5) = 3
core_number( 6) = 3
core_number( 7) = 3
core_number( 8) = 3
core_number( 9) = 2
core_number(10) = 3
core_number(11) = 2
core_number(12) = 1
core_number(13) = 1
core_number(14) = 3
core_number(15) = 3
core_number(16) = 0
core_number(17) = 2
core_number(18) = 2
core_number(19) = 2
core_number(20) = 2
core_number(21) = 1
The output shows us that vertex 16 is the only vertex with a core number of 0. Because this graph is an example, "it just
so happens" that the cores make a nice picture. The following code plots the convex hull around the points inside of a k-core.
The darker the color, the higher the core.
clf; hold on; wh=ones(1,3); colors={0.85*wh,0.7*wh,0.55*wh,0.4*wh};
for c=unique(cn')
m=cn>=c; cl=colors{c+1}; lw=16*1.5^(max(cn)-c);
xym=xy(m,:);is=convhull(xym(:,1),xym(:,2));
h=fill(xym(is,1),xym(is,2),cl);set(h,'LineWidth',lw,'EdgeColor',cl);
end
gplot(A,xy,'k-'); plot(xy(:,1), xy(:,2),'r.','MarkerSize',24);
text(xy(:,1)+0.1, xy(:,2)+0.1, num2str((1:21)'));set(gcf,'Color',[1,1,1]);
xlim([-1,10]);ylim([-2,7]);axis off; hold off;
The figure shows the 0-core, the 1-core, the 2-core and the 3-core. Although vertex 11 has degree 5, it is only a 2-core vertex
because it links to two 1-core vertices. Also, note that the cores aren't necessarily connected components. The 2-core contains
two connected components (2-11,14-15) and (17-20).
With MatlabBGL
The MatlabBGL function core_numbers implements efficient algorithms to compute the cores of a graph. These algorithms are
significantly more efficient than the previous code and produce identical output.
Let's check that they produce the same output.
cn_bgl = core_numbers(A);
any(cn_bgl-cn)
ans =
0
The core numbers of a road network
For fun, let's see how many cores there are in a road network. Vertices in a 1-core in a road network have at least one path
between them (assuming the underlying network is connected). Vertices in a 2-core have at least two paths between them.
load('../graphs/minnesota.mat');A=spones(A);
[cn csz]=core_numbers(A); cs=unique(cn);
arrayfun(@(v,c) fprintf('core(%2i) = %4i\n',v,c),cs,csz(cs+1));
d2v=find(sum(A,2)==2);
while ~isempty(d2v)
for v=d2v'
l=find(A(:,v));A(l(1),v)=0;A(l(2),v)=0;A(v,l(1))=0;A(v,l(2))=0;
A(l(1),l(2))=1;A(l(2),l(1))=1;
end
d2v=find(sum(A,2)==2);
end
max(core_numbers(A))
core( 1) = 2
core( 2) = 98
ans =
2
The highest core is still two! I think that's pretty amazing. There aren't subnetworks of the Minnesota highways where you
always have at least three choices at every intersection.