Background
This challenge is about the stochastic block model. Basically, you are given an undirected graph, where the nodes represent people, and the edges represent social connections between them. The people are divided into two "communities", and two people are more likely to have a connection if they are in the same community. Your task is to guess these hidden communities based on the connections.
Input
Your input is a 2D array of bits of size 100×100. It represents the adjacency matrix of an undirected graph of size 100. The graph is generated by the following random process:
- Each node is placed in community 0 with probability 0.5, otherwise it's placed in community 1. The communities are not visible in the input.
- For each pair of distinct nodes that are in the same community, put an edge between them with probability 0.3.
- For each pair of distinct nodes that are in different communities, put an edge between them with probability 0.1.
All random choices are independent.
Output
Your output is a 1D array of bits of length 100. It represents your program's guess about the underlying communities. You are not required to correctly guess which nodes belong to community 0 and which to community 1 (which would be impossible), just the partition into two communities.
Scoring
In this repository, you'll find a text file containing 30 lines. Each line represents a graph generated by the above process, and it consists of the array of the hidden communities, a semicolon, and a comma-separated list of rows of the adjacency matrix. You should read this text file, call your submission program on the adjacency matrix of each line, and compare its output to the underlying community structure.
Your submission's score on a particular graph is
min(sum_i(V[i] == W[i]), sum_i(V[i] == 1-W[i]))
where V
is the array of hidden communities, W
is your program's guess based on the adjacency matrix, and i
ranges over their indices.
Basically, this measures the number of mis-classified nodes, taking into account that you're not required to guess which community is which.
The lowest average score over all 30 test cases wins, with byte count used as a tie breaker.
Example
Consider the input matrix (6×6 for clarity)
011000
101100
110000
010011
000101
000110
It corresponds to the 6-node graph
a-b-d
|/ /|
c e-f
consisting of two triangles with one edge between them.
Your program might guess that each triangle is a separate community, and output 000111
, where the first three nodes are in community 0 and the rest in community 1.
The output 111000
is equivalent, since the order of the communities doesn't matter.
If the true community structure was 001111
, then your score would be 1
, because one node is mis-classified.
Additional rules
Your program should take no more than 5 seconds on any single test case on a reasonably modern computer (I'm unable to test most answers, so I'll just trust you here).
You can write a full program or a function, and standard loopholes are disallowed. You are allowed to optimize your answer toward the parameters used here (size 100, probabilities 0.3 and 0.1), but not the actual test cases. I keep the right to generate a new set of graphs if I suspect foul play.
Test program
Here's a Python 3 function that computes the score of a Python 3 function submission.
import time
def test_from_file(submission, filename):
f = open(filename)
num_graphs = 0
total_score = 0
for line in f:
num_graphs += 1
a, _, c = line.partition(';')
V = [int(node) for node in a]
E = [[int(node) for node in row] for row in c[:-1].split(',')]
start = time.perf_counter()
W = submission(E)
end = time.perf_counter()
x = sum(V[i]==W[i] for i in range(len(V)))
score = min(x, len(V) - x)
print("Scored {0:>2d} in {1:f} seconds".format(score, end - start))
total_score += score
f.close()
return total_score / num_graphs