Overlapping blocks for bipartite LayeredBlockState with clabel

Hi Tiago/all,

I am working with a bipartite graph with signed edges (+/- 1), which I treat as edge covariates for estimating a nested LayeredBlockState model:

state = gt.minimize_nested_blockmodel_dl(
        g,
        state_args=dict(
            base_type=gt.LayeredBlockState,
            clabel = clabel, # impose hard bipartite constraint
            pclabel= clabel, 
            state_args=dict(ec=g.ep.weight, # in {-1, 1}
                            layers= False,
                            deg_corr= True,
                            overlap = True,
                            )))

It works excellently in the non-overlapping case and fings ~150 blocks at the lowest level, so I know the graph construction is good. But when I run the overlapping case with the hard bipartite constraint I get odd results: every half-edge is only classified in one of two blocks {1,0}, which map to the two node types of the network. Running it without the constraint (clabel = pclabel = None) yields much more intuitive results – many overlapping blocks – but takes longer to converge and does not guarantee fully bipartite blocks, so it seems non-optimal.

Any ideas on what might be causing this? Thanks very much.

Best,
Galen Hall

Please do not ignore the instructions that appear when you make a new post: without a minimal working example that shows the problem, there’s little we can do to help.

Can you provide a complete example, including the graph you’re using, that shows the issue you’re encountering?

Apologies for both the missing minimal working example and the long response time. Here is a minimal working example; it in fact doesn’t depend on the graph being bipartite, but just on the presence of pclabel and clabel in the blockstate constructor. I noticed that in the replication code for your paper on topic models using SBMs you correct for this with a remove_redundant_levels() function…

import graph_tool.all as gt

g = gt.collection.ns["new_guinea_tribes"]

state = gt.minimize_nested_blockmodel_dl(g,
                                         state_args=dict(base_type=gt.LayeredBlockState,
                                                         state_args=dict(ec=g.ep.weight, layers=False)))

## Create new labels using the inferred partition
kind = g.vp["kind"] = g.new_vp("int")
for v, l in zip(g.get_vertices(), state.project_partition(0,0)):
  kind[v] = l

## Minimize the nested blockmodel again, this time with pclabel
state = gt.minimize_nested_blockmodel_dl(g,
                                         state_args=dict(base_type=gt.LayeredBlockState,
                                                         pclabel = g.vp.kind, clabel = g.vp.kind,
                                                         state_args=dict(ec=g.ep.weight, layers=False)))

state.draw(layout='bipartite')

Which draws the following:

What is the problem? What did you expect to find that you didn’t?