Source code for pyrigi.graph._flexibility.nac.mono_classes
"""This module is responsible for findingtriangle-connected components and:prf:ref:`NAC-mono classes <def-nac-mono>`."""fromcollectionsimportdefaultdictfromenumimportEnumimportnetworkxasnxfrompyrigi._utils.union_findimportUnionFindfrompyrigi.data_typeimportEdge
[docs]classMonoClassType(Enum):""" Represents approaches for finding :prf:ref:`NAC-mono classes <def-nac-mono>` of different types - single edges, triangle-connected components, and triangle-extended classes. Suggested Improvement --------------------- Make from_string more fancy. """""" Each edge is its own :prf:ref:`NAC-mono class <def-nac-mono>`. """EDGES="edges"""" Corresponds to :prf:ref:`\\triangle-connected components <def-triangle-connected-comp>`. """TRI_CONNECTED="triangle"""" Corresponds to :prf:ref:`\\hat\\triangle-extended classes<def-triangle-extended-class>`. """TRI_EXTENDED="triangle-extended"
[docs]@staticmethoddeffrom_string(class_name:str)->"MonoClassType":matchclass_name:case"edges":returnMonoClassType.EDGEScase"triangle":returnMonoClassType.TRI_CONNECTEDcase"triangle-extended":returnMonoClassType.TRI_EXTENDEDcase_:raiseValueError(f"Unknown NAC-mono class type: {class_name}")
def_trivial_mono_classes(graph:nx.Graph,)->tuple[dict[Edge,int],list[list[Edge]]]:""" Make each edge its own NAC-mono class. """edge_to_class:dict[Edge,int]={}class_to_edges:list[list[Edge]]=[]fori,einenumerate(graph.edges):edge_to_class[e]=iclass_to_edges.append([e])returnedge_to_class,class_to_edges
[docs]deffind_mono_classes(graph:nx.Graph,class_type:MonoClassType=MonoClassType.TRI_EXTENDED,)->tuple[dict[Edge,int],list[list[Edge]]]:""" Find :prf:ref:`NAC-mono classes <def-nac-mono>` based on the type given. First, all the classes of triangle equivalence are found. Then these are optionally extended to larger NAC-mono classes as described in :cite:p:`LastovickaLegersky2024`. An ID of a :prf:ref:`NAC-mono class <def-nac-mono>` corresponds to its index in a list of all NAC-mono classes. A mapping from edges to their class ID is returned together with a list of NAC-mono classes where the index corresponds to the class ID. Parameters ---------- class_type: Type of :prf:ref:`NAC-mono classes <def-nac-mono>` """ifclass_type==MonoClassType.EDGES:return_trivial_mono_classes(graph)classes=UnionFind()# Finds trianglesforedgeingraph.edges:v,u=edge# We cannot sort vertices and we cannot expect# any regularity or order of the verticesclasses.join((u,v),(v,u))v_neighbours=set(graph.neighbors(v))u_neighbours=set(graph.neighbors(u))intersection=v_neighbours.intersection(u_neighbours)forwinintersection:classes.join((u,v),(w,v))classes.join((u,v),(w,u))# Checks for edges & triangles over class# This MUST be run before searching for squares for Cartesian NAC-coloring# or other searches that may produce disconnected classes,# as cycles may not exist!# These routines are highly inefficient, but the time is still# negligible compared to the main algorithm running time.ifclass_type==MonoClassType.TRI_EXTENDED:# we try again until we find no other class to merge# new opinions may appear later# could be most probably implemented smarterdone=Falsewhilenotdone:done=Truevertex_to_classes:list[set[Edge]]=[set()for_inrange(max(graph.nodes)+1)]# prepare updated vertex to class mappingforeingraph.edges:class_id=classes.find(e)vertex_to_classes[e[0]].add(class_id)vertex_to_classes[e[1]].add(class_id)# v is the top of the triangle over classforvingraph.nodes:# maps class to set of vertices containing itclass_to_vertices:dict[Edge,set[int]]=defaultdict(set)foruingraph.neighbors(v):# find all the classes v neighbors withforclass_of_vertexinvertex_to_classes[u]:class_to_vertices[class_of_vertex].add(u)# if we found more edges to the same class,# we also found a triangle and we merge its armsforclass_of_vertex,verticesinclass_to_vertices.items():ifclass_of_vertex==len(vertices)<=1:continuevertices=iter(vertices)w=next(vertices)foruinvertices:# if something changed, we may have another# change for improvement the next rounddone&=notclasses.join((v,w),(v,u))edge_to_class:dict[Edge,int]={}class_to_edge:list[list[Edge]]=[]foredgeingraph.edges:root=classes.find(edge)ifrootnotinedge_to_class:edge_to_class[root]=id=len(class_to_edge)class_to_edge.append([])else:id=edge_to_class[root]edge_to_class[edge]=idclass_to_edge[id].append(edge)returnedge_to_class,class_to_edge