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.

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')