[docs]deffind_sccs(graph:dict[any,list[any]]):""" Takes a dictionary of nodes (can be anything) that map to a list of nodes. The list represents the outgoing edges. returns: list[list[any]] The algorithm returns a list of lists of nodes. The outer list is topologically sorted and the inner list are groups of strongly-connected components. """ordnum=0stack=[]low_link={}visited=set()sccs=[]def_dfs(v):nonlocalordnumv_ordnum=ordnumlow_link[v]=ordnumordnum+=1stack.append(v)visited.add(v)forneighboringraph[v]:ifneighbornotinlow_link:_dfs(neighbor)low_link[v]=min(low_link[v],low_link[neighbor])elifneighborinstack:low_link[v]=min(low_link[v],low_link[neighbor])iflow_link[v]==v_ordnum:scc=[]whileTrue:u=stack.pop()scc.append(u)ifu==v:breaksccs.append(scc)forvingraph:ifvnotinlow_link:_dfs(v)returnsccs
[docs]deffind_sccs_matrix(matrix:np.array):""" Takes a matrix. Nonzero elements represent edges. returns: list[list[int]] The algorithm returns a list of lists of columns. The outer list is topologically sorted and the inner list are groups of strongly-connected components of the edges given by the matrix. """assert(len(matrix.shape)==2)assert(matrix.shape[0]==matrix.shape[1])n=matrix.shape[0]ordnum=0stack=[]low_link=np.empty(shape=(n,),dtype=int)low_link[:]=-1visited=np.zeros(shape=(n,),dtype=bool)sccs=[]def_dfs(col):nonlocalordnumv_ordnum=ordnumlow_link[col]=ordnumordnum+=1stack.append(col)visited[col]=Trueforrow,valinenumerate(matrix[:,col]):ifval==0:continueiflow_link[row]==-1:_dfs(row)low_link[col]=min(low_link[col],low_link[row])elifrowinstack:low_link[col]=min(low_link[col],low_link[row])iflow_link[col]==v_ordnum:scc=[]whileTrue:u=stack.pop()scc.append(u)ifu==col:breaksccs.append(scc)forcolinrange(n):iflow_link[col]==-1:_dfs(col)returnsccs