def get_fbvs_max_size(self, g: MultiGraph, k: int) -> set: if len(g) <= k + 2: return set(g.nodes()[:k]) # Construct a trivial FVS of size k + 1 on the first k + 3 vertices of G. nodes = g.nodes() # The set of nodes currently under consideration. node_set = set(nodes[:(k + 2)]) # The current best solution, of size (k + 1) before each compression step, # and size <= k at the end. soln = set(nodes[:k]) for i in range(k + 2, len(nodes)): soln.add(nodes[i]) node_set.add(nodes[i]) if len(soln) < k + 1: continue assert (len(soln) == (k + 1)) assert (len(node_set) == (i + 1)) new_soln = self.ic_compression(g.subgraph(node_set), soln, k) if new_soln is None: return None soln = new_soln assert (len(soln) <= k) return soln
def simplify(streckennetz: nx.MultiGraph) -> nx.MultiGraph: print('simplifying...') # Remove the shorter edge of two parallel edges edges_to_remove = [] for edges_index in streckennetz.edges(): edges = streckennetz[edges_index[0]][edges_index[1]] if len(edges) > 1: min_length = edges[0]['length'] min_length_index = 0 for i in edges: if edges[i]['length'] < min_length: min_length = edges[i]['length'] min_length_index = i edges_to_remove.append(list(edges_index) + [min_length_index]) print('found', len(edges_to_remove), 'parallel edges') streckennetz.remove_edges_from(edges_to_remove) # Remove nodes, that only have a single edge while True: nodes_to_remove = [] for node in streckennetz.nodes(): if streckennetz.degree( node) < 2 and 'type' not in streckennetz.nodes[node]: nodes_to_remove.append(node) if nodes_to_remove: print('found', len(nodes_to_remove), 'deadend nodes') streckennetz.remove_nodes_from(nodes_to_remove) nodes_to_remove = [] else: break return streckennetz
def get_graph_extents( networkX_multigraph: nx.MultiGraph ) -> Tuple[float, float, float, float]: """ Parameters ---------- networkX_multigraph A `NetworkX` `MultiGraph` with `x` and `y` node parameters. Returns ------- Tuple A tuple of `min_x`, `min_y`, `max_x`, `max_y` values. """ # get min and maxes for x and y min_x = np.inf max_x = -np.inf min_y = np.inf max_y = -np.inf for n, d in networkX_multigraph.nodes(data=True): if d['x'] < min_x: min_x = d['x'] if d['x'] > max_x: max_x = d['x'] if d['y'] < min_y: min_y = d['y'] if d['y'] > max_y: max_y = d['y'] return min_x, min_y, max_x, max_y
def reduction2(g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool): for v in h.nodes(): # Check if G[W ∪ {v}] contains a cycle. if not is_forest(g.subgraph(w.union({v}))): g.remove_node(v) h.remove_nodes_from([v]) return (k - 1, v, True) return (k, None, False)
def reduction1(g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool): changed = False for v in g.nodes(): if g.degree(v) <= 1: g.remove_node(v) h.remove_nodes_from([v]) changed = True return (k, None, changed)
def test_relabel_nodes_multigraph(): """failed after switching to dg.relabel_nodes""" G = MultiGraph([('a', 'b'), ('a', 'b')]) mapping = {'a': 'aardvark', 'b': 'bear'} G = relabel_nodes(G, mapping, copy=False) assert sorted(G.nodes()) == ['aardvark', 'bear'] assert_edges_equal(sorted(G.edges()), [('aardvark', 'bear'), ('aardvark', 'bear')])
def graph_minus(g: MultiGraph, w: set) -> MultiGraph: gx = MultiGraph() for (n1, n2) in g.edges(): if n1 not in w and n2 not in w: gx.add_edge(n1, n2) for n in g.nodes(): if n not in w: gx.add_node(n) return gx
def reduction3(self, g: MultiGraph, k: int) -> (int, List[int], bool): """ If there is a vertex v of degree at most 1, delete v. """ changed = False for v in g.nodes(): if g.degree(v) <= 1: g.remove_node(v) changed = True return k, None, changed
def reduction1(self, g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool): """ Delete all nodes of degree 0 or 1 as they can't be part of any cycles. """ changed = False for v in g.nodes(): if g.degree(v) <= 1: g.remove_node(v) h.remove_nodes_from([v]) changed = True return k, None, changed
def get_node_feature_matrix(self, G:nx.MultiGraph, doc, embd_dim=96, embedding_type=None, as_torch=True, is_padding_pos=True, **kwargs): """ Returns X with shape [num_nodes, node_feat_dim] """ assert G is not None NDV = G.nodes(data=True); NV = G.nodes(data=False) _is_head_node = lambda x: 'obj' in x head_nodes = list(filter(_is_head_node, NV)) objs = self.clevr_parser.filter_clevr_objs(doc.ents) N, M = len(NDV), (embd_dim+3) if is_padding_pos else embd_dim feat_mats = [] for i, entity in enumerate(objs): if entity.label_ not in ('CLEVR_OBJS', 'CLEVR_OBJ'): continue ent_mat = self.clevr_parser.get_clevr_entity_matrix_embedding(entity, dim=embd_dim, include_obj_node_emd=True, is_padding_pos=is_padding_pos) ent_mat = ent_mat.reshape((-1, embd_dim)) if is_padding_pos: head_node = G.nodes.get(head_nodes[i]) pos = head_node.get('pos') # pos = (x,y,z): Tuple[float] if not pos: pos = (0.0, 0.0, 0.0) pos = np.tile(np.asarray(pos, dtype=float), ent_mat.shape[0]).reshape(-1, 3) # np.pad(ent_mat, ((0,0), (0, 3)), 'constant', constant_values=(0, 0.0) ) ent_mat = np.concatenate((ent_mat, pos), axis=1) # [n, embd_dim+3] feat_mats.append(ent_mat) if len(feat_mats) > 1: feat_mats = reduce(lambda a, b: np.vstack((a, b)), feat_mats) else: feat_mats = feat_mats[0] assert feat_mats.shape == (N, M) if as_torch: feat_mats = torch.from_numpy(feat_mats).float() if kwargs.get('is_cuda'): device = 'cuda' if torch.cuda.is_available() else 'cpu' feat_mats = feat_mats.to(device) return feat_mats
def reduction3(g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool): for v in h.nodes(): if g.degree(v) == 2: # If v has a neighbour in H, short-curcuit it. if len(h[v]) >= 1: # Delete v and make its neighbors adjacent. [n1, n2] = g.neighbors(v) g.remove_node(v) g.add_edge(n1, n2) # Update H accordingly. h.remove_nodes_from([v]) if n1 not in w and n2 not in w: h.add_edge(n1, n2) return (k, None, True) return (k, None, False)
def subgraph_by_timestamp(mg: nx.MultiGraph, start: int, end: int) -> nx.Graph: edges = filter( lambda edge: start <= edge[2]["timestamp"] and edge[2]["timestamp"] < end, mg.edges(data=True), ) g = nx.Graph() for node in mg.nodes(): g.add_node(node) for u, v, data in edges: if g.has_edge(u, v): g[u][v]["weight"] += data["weight"] else: g.add_edge(u, v, weight=data["weight"]) return g
def reduction2(self, g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool): """ If there exists a node v in H such that G[W ∪ {v}] contains a cycle, then include v in the solution, delete v and decrease the parameter by 1. That is, the new instance is (G - {v}, W, k - 1). If v introduces a cycle, it must be part of X as none of the vertices in W will be available to neutralise this cycle. """ for v in h.nodes(): # Check if G[W ∪ {v}] contains a cycle. if not is_acyclic(g.subgraph(w.union({v}))): g.remove_node(v) h.remove_nodes_from([v]) return k - 1, v, True return k, None, False
def update_graph_with_spatial_re(cls, G: nx.MultiGraph, doc, **kwargs) -> nx.MultiGraph: spatial_res = cls.filter_spatial_re(doc.ents) if spatial_res is None: return G NV = G.nodes(data=False) _is_head_node = lambda x: 'obj' in x head_nodes = list(filter(_is_head_node, NV)) objs = cls.filter_clevr_objs(doc.ents) relations = cls.extract_spatial_relations(doc) assert len(objs) == len(head_nodes) assert len(relations) == len(spatial_res) o2n_map = dict(zip(objs, head_nodes)) sr2r_map = dict(zip(spatial_res, relations)) def add_nodes(n0, n1, r): if not G.has_edge(*(n0, n1, 'spatial_re')): #G.add_edge(n0, n1, **{'label':'spatial_re', 'val': r}) G.add_edge(n0, n1, spatial_re=r) # G.add_edge(n0, n1,key=r) has_and = 'and' in doc.text # has_or = 'or' in doc.text # is_logical_re = has_and or has_or for i, ent in enumerate(doc.ents): if ent in objs: continue if ent in spatial_res: _r = sr2r_map[ent] try: if i > 1: if has_and: n1 = o2n_map[doc.ents[i + 1]] n0 = o2n_map[doc.ents[0]] else: n0 = o2n_map[doc.ents[i - 1]] n1 = o2n_map[doc.ents[i + 1]] else: n0 = o2n_map[doc.ents[i - 1]] n1 = o2n_map[doc.ents[i + 1]] add_nodes(n0, n1, _r) except IndexError as ie: print(ie) except UnboundLocalError as lr: print(lr) return G
def connect_matching_pair_edges(self, Gu: nx.MultiGraph, ls, rs, obj_node_id='obj', connect_obj_rel_edges=False) -> nx.Graph: """ Ground truth (_gt_)generator function for a combined graph. Used for training S_0 :param Gu: A (unconnected) composed graph of Gs, Gt. No relational links between head nodes, thus, the number_connected_components(Gs|Gt) = number of object nodes in each graph. :param Gs: The source Graph, i.e., the text graph representation :param Gt: The target Graph, i.e., the grounding (image features) graph representation :param obj_node_id: the identifier determining a obj (or head) node :return: matching_pairs: List of matching pair tuples between Gs, Gt """ matching_pairs, unmatched_pairs = self.get_matching_pairs_in_bipartite_graph(Gu, ls, rs, obj_node_id) NDV = Gu.nodes(data=True) # Connect the matching pairs if not connected # for pair in matching_pairs: s_node, t_node = pair if not Gu.has_edge(s_node, t_node): Gu.add_edge(s_node, t_node, '<gt>') # Connect Attr Nodes # is_head_node = lambda x: obj_node_id in x Ns = nx.neighbors(Gu, s_node) Nt = list(nx.neighbors(Gu, t_node)) for ns in Ns: if is_head_node(ns): continue # Check label equality only, 'val' equality already verified ns_label = NDV[ns]['label'] #logger.debug(f'Source attr node label = {ns_label}') # TODO: potential issue here, Nt should always have a matching attr node for nt in filter(lambda x: NDV[x]['label'] == ns_label, Nt): if not Gu.has_edge(ns, nt): Gu.add_edge(ns, nt, key='<gt>') if connect_obj_rel_edges: # TODO: Add a obj relation edge among all Gs, Gt obj nodes. Actually # should be done earlier in the parse cycle. pass return Gu
def reduction3(self, g: MultiGraph, w: set, h: MultiGraph, k: int) -> (int, int, bool): """ If there is a node v ∈ V(H) of degree 2 in G such that at least one neighbor of v in G is from V (H), then delete this node and make its neighbors adjacent (even if they were adjacent before; the graph could become a multigraph now). """ for v in h.nodes(): if g.degree(v) == 2: # If v has a neighbour in H, short-curcuit it. if len(h[v]) >= 1: # Delete v and make its neighbors adjacent. [n1, n2] = g.neighbors(v) g.remove_node(v) g.add_edge(n1, n2) # Update H accordingly. h.remove_nodes_from([v]) if n1 not in w and n2 not in w: h.add_edge(n1, n2) return k, None, True return k, None, False
def build_node(g: nx.MultiGraph, new_node: int, n: int, const_amt: int, fee_type: str) -> List: selected = [] # 2 initial edges so routes can pass through new node while len(selected) < n: max_reward = 0 best_channel = None # try all channels for node in g.nodes(): if node == new_node or node in g[new_node]: continue # create new channel g.add_edge_with_init(new_node, node, default=True) # calculate max reward reward, fee = maximise_fee( g, (new_node, node), const_amt, fee_type) # select channel if highest reward if max_reward <= reward: max_reward = reward best_channel = (node, fee) # reset graph for next channel g.remove_edge(new_node, node) if best_channel is None: raise Exception('Profit not possible.') # add selected channel and redo for next channel selected.append((new_node, best_channel[0], best_channel[1])) g.add_edge_with_init(new_node, best_channel[0], default=True) g.update_fee(new_node, best_channel[0], best_channel[1], fee_type) # return node with selected channels return selected
def reduction4(self, g: MultiGraph, k: int) -> (int, List[int], bool): """ If there is a vertex v of degree 2, delete v and connect its two neighbors by a new edge. """ for v in g.nodes(): if g.degree(v) == 2: # Delete v and make its neighbors adjacent. ne = g.neighbors(v) # We must check whether v has 2 neighbors, or just one but connected to v by multiple edges if len(ne) == 2: [n1, n2] = ne else: [n1] = ne n2 = n1 g.remove_node(v) # Only add the edge if there are currently less than 2 edges between these two nodes es = g[n1].get(n2, {}) if len(es) < 2: g.add_edge(n1, n2) return k, None, True return k, None, False
def preprocess_2(self, g: MultiGraph, f: set, active_v) -> set: mif_set = set() while not is_independent_set(g, f): mif_set = mif_set.union(f) for component in connected_components(g.subgraph(f)): if len(component) > 1: if active_v in component: active_v = component.pop() compressed_node = active_v else: compressed_node = component.pop() g = self.compress(g, component, compressed_node, False) f = f.intersection(g.nodes()) # Maybe faster with # f = f.difference(component) # f.add(compressed_node) mif_set = mif_set.union(component) break mif_set2 = self.mif_main(g, f, active_v) if mif_set2: mif_set = mif_set2.union(mif_set) return mif_set
def mif_preprocess_2(g: MultiGraph, f: set, active_v, k: int) -> set: mif_set = set() while not is_independent_set(g, f): mif_set = mif_set.union(f) for component in nxc.connected_components(g.subgraph(f)): if len(component) > 1: if active_v in component: active_v = component.pop() compressed_node = active_v else: compressed_node = component.pop() g = compress(g, component, compressed_node, True) f = f.intersection(g.nodes()) # Maybe faster with # f = f.difference(component) # f.add(compressed_node) mif_set = mif_set.union(component) break mif_set2 = mif_main(g, f, active_v, k) if mif_set2: mif_set = mif_set2.union(mif_set) if k == None or len(mif_set) >= k: return mif_set return None
class ShuttlingGraph(list): def __init__(self, shuttlingEdges=list() ): super(ShuttlingGraph, self).__init__(shuttlingEdges) self.currentPosition = None self.currentPositionName = None self.nodeLookup = dict() self.currentPositionObservable = Observable() self.graphChangedObservable = Observable() self.initGraph() self._hasChanged = True def initGraph(self): self.shuttlingGraph = MultiGraph() for edge in self: self.shuttlingGraph.add_node(edge.startName) self.shuttlingGraph.add_node(edge.stopName) self.shuttlingGraph.add_edge(edge.startName, edge.stopName, key=hash(edge), edge=edge, weight=abs(edge.stopLine-edge.startLine)) self.nodeLookup[edge.startLine] = edge.startName self.nodeLookup[edge.stopLine] = edge.stopName def rgenerateNodeLookup(self): self.nodeLookup.clear() for edge in self: self.nodeLookup[edge.startLine] = edge.startName self.nodeLookup[edge.stopLine] = edge.stopName @property def hasChanged(self): return self._hasChanged @hasChanged.setter def hasChanged(self, value): self._hasChanged = value def position(self, line): return self.nodeLookup.get(line) def setPosition(self, line): if self.currentPosition!=line: self.currentPosition = line self.currentPositionName = self.position(line) self.currentPositionObservable.fire( line=line, text=firstNotNone(self.currentPositionName, "") ) def getMatchingPosition(self,graph): """Try to match node name/position to the current settings in the provided ShuttlingGraph.""" if not graph: return self.currentPosition # no change # Matching node name. Need to set the corresponding position for edge in self: if edge.startName == graph.currentPositionName: return edge.startLine if edge.stopName == graph.currentPositionName: return edge.stopLine #if graph.currentPosition: # return graph.currentPosition #just use the graph's position return self.currentPosition def addEdge(self, edge): self._hasChanged = True self.append(edge) self.shuttlingGraph.add_edge(edge.startName, edge.stopName, key=hash(edge), edge=edge, weight=abs(edge.stopLine-edge.startLine)) self.nodeLookup[edge.startLine] = edge.startName self.nodeLookup[edge.stopLine] = edge.stopName self.graphChangedObservable.firebare() self.setPosition(self.currentPosition) def isValidEdge(self, edge): return ((edge.startLine not in self.nodeLookup or self.nodeLookup[edge.startLine] == edge.startName) and (edge.stopLine not in self.nodeLookup or self.nodeLookup[edge.stopLine] == edge.stopName)) def getValidEdge(self): index = 0 while self.shuttlingGraph.has_node("Start_{0}".format(index)): index += 1 startName = "Start_{0}".format(index) index = 0 while self.shuttlingGraph.has_node("Stop_{0}".format(index)): index += 1 stopName = "Stop_{0}".format(index) index = 0 startLine = (max( self.nodeLookup.keys() )+1) if self.nodeLookup else 1 stopLine = startLine + 1 return ShuttleEdge(startName, stopName, startLine, stopLine, 0, 0, 0, 0) def removeEdge(self, edgeno): self._hasChanged = True edge = self.pop(edgeno) self.shuttlingGraph.remove_edge(edge.startName, edge.stopName, hash(edge)) if self.shuttlingGraph.degree(edge.startName) == 0: self.shuttlingGraph.remove_node(edge.startName) if self.shuttlingGraph.degree(edge.stopName) == 0: self.shuttlingGraph.remove_node(edge.stopName) self.graphChangedObservable.firebare() self.rgenerateNodeLookup() self.setPosition(self.currentPosition) def setStartName(self, edgeno, startName): self._hasChanged = True startName = str(startName) edge = self[edgeno] if edge.startName != startName: self.shuttlingGraph.remove_edge(edge.startName, edge.stopName, key=hash(edge)) if self.shuttlingGraph.degree(edge.startName) == 0: self.shuttlingGraph.remove_node(edge.startName) edge.startName = startName self.shuttlingGraph.add_edge(edge.startName, edge.stopName, key=hash(edge), edge=edge, weight=abs(edge.stopLine-edge.startLine) ) self.graphChangedObservable.firebare() self.setPosition(self.currentPosition) self.rgenerateNodeLookup() return True def setStopName(self, edgeno, stopName): self._hasChanged = True stopName = str(stopName) edge = self[edgeno] if edge.stopName != stopName: self.shuttlingGraph.remove_edge(edge.startName, edge.stopName, key=hash(edge)) if self.shuttlingGraph.degree(edge.stopName) == 0: self.shuttlingGraph.remove_node(edge.stopName) edge.stopName = stopName self.shuttlingGraph.add_edge(edge.startName, edge.stopName, key=hash(edge), edge=edge, weight=abs(edge.stopLine-edge.startLine) ) self.graphChangedObservable.firebare() self.rgenerateNodeLookup() self.setPosition(self.currentPosition) return True def setStartLine(self, edgeno, startLine): self._hasChanged = True edge = self[edgeno] if startLine != edge.startLine and (startLine not in self.nodeLookup or self.nodeLookup[startLine] == edge.startName): self.nodeLookup.pop(edge.startLine) edge.startLine = startLine self.shuttlingGraph.edge[edge.startName][edge.stopName][hash(edge)]['weight'] = abs(edge.stopLine-edge.startLine) self.rgenerateNodeLookup() self.graphChangedObservable.firebare() self.setPosition(self.currentPosition) return True return False def setStopLine(self, edgeno, stopLine): self._hasChanged = True edge = self[edgeno] if stopLine != edge.stopLine and (stopLine not in self.nodeLookup or self.nodeLookup[stopLine] == edge.stopName): self.nodeLookup.pop(edge.stopLine) edge.stopLine = stopLine self.shuttlingGraph.edge[edge.startName][edge.stopName][hash(edge)]['weight'] = abs(edge.stopLine-edge.startLine) self.rgenerateNodeLookup() self.graphChangedObservable.firebare() self.setPosition(self.currentPosition) return True return False def setIdleCount(self, edgeno, idleCount): self._hasChanged = True self[edgeno].idleCount = idleCount return True def setSteps(self, edgeno, steps): self._hasChanged = True self[edgeno].steps = steps return True def shuttlePath(self, fromName, toName ): fromName = firstNotNone(fromName, self.currentPositionName) fromName = fromName if fromName else self.position(float(self.currentPosition)) if fromName not in self.shuttlingGraph: raise ShuttlingGraphException("Shuttling failed, origin '{0}' is not a valid shuttling node".format(fromName)) if toName not in self.shuttlingGraph: raise ShuttlingGraphException("Shuttling failed, target '{0}' is not a valid shuttling node".format(toName)) sp = shortest_path(self.shuttlingGraph, fromName, toName) path = list() for a, b in pairs_iter(sp): edge = sorted(self.shuttlingGraph.edge[a][b].values(), key=itemgetter('weight'))[0]['edge'] path.append((a, b, edge, self.index(edge))) return path def nodes(self): return self.shuttlingGraph.nodes() def toXmlElement(self, root): mydict = dict( ( (key, str(getattr(self, key))) for key in ('currentPosition', 'currentPositionName') if getattr(self, key) is not None ) ) myElement = ElementTree.SubElement(root, "ShuttlingGraph", attrib=mydict ) for edge in self: edge.toXmlElement( myElement ) return myElement def setStartType(self, edgeno, Type): self._hasChanged = True self[edgeno].startType = str(Type) return True def setStopType(self, edgeno, Type): self._hasChanged = True self[edgeno].stopType = str(Type) return True def setStartLength(self, edgeno, length): edge = self[edgeno] if length!=edge.startLength: if length+edge.stopLength<edge.sampleCount: self._hasChanged = True edge.startLength = int(length) else: return False return True def setStopLength(self, edgeno, length): edge = self[edgeno] if length!=edge.stopLength: if edge.startLength+length<edge.sampleCount: self._hasChanged = True edge.stopLength = int(length) else: return False return True @staticmethod def fromXmlElement( element ): edgeElementList = element.findall("ShuttleEdge") edgeList = [ ShuttleEdge.fromXmlElement(e) for e in edgeElementList ] return ShuttlingGraph(edgeList)
def mif_main(g: MultiGraph, f: set, t, k: int) -> set: k_set = k != None new_k1 = new_k2 = None if k_set and k > g.order(): return None if f == g.nodes() or (k_set and k <= 0): return f if (not f): g_degree = g.degree() g_max_degree_node = max(g_degree, key=lambda n: g_degree[n]) if (g_degree[g_max_degree_node] <= 1): return set(g.nodes()) else: fx = f.copy() fx.add(g_max_degree_node) gx = g.copy() gx.remove_node(g_max_degree_node) if k_set: new_k1 = k-1 new_k2 = k mif_set1 = mif_preprocess_1(g, fx, t, new_k1) mif_set2 = mif_preprocess_1(gx, f, t, new_k2) if not mif_set1: return mif_set2 elif not mif_set2: return mif_set1 else: return max(mif_set1, mif_set2, key=len) # Set t as active vertex if t == None or not t in f: t = next(iter(f)) gd_over_3 = None gd_2 = None for v in g.neighbors_iter(t): (gd_v, gn_v) = generalized_degree(g, f, t, v) if gd_v <= 1: f.add(v) if k_set: new_k1 = k-1 return mif_preprocess_1(g, f, t, new_k1) elif gd_v >= 3: gd_over_3 = v else: gd_2 = (v, gn_v) if gd_over_3 != None: # Cannot simply use "if gd_over_3" because v might be 0 fx = f.copy() fx.add(gd_over_3) gx = g.copy() gx.remove_node(gd_over_3) if k_set: new_k1 = k-1 new_k2 = k mif_set1 = mif_preprocess_1(g, fx, t, new_k1) mif_set2 = mif_preprocess_1(gx, f, t, new_k2) if not mif_set1: return mif_set2 elif not mif_set2: return mif_set1 else: return max(mif_set1, mif_set2, key=len) elif gd_2 != None: (v, gn) = gd_2 fx1 = f.copy() fx2 = f.copy() fx1.add(v) for n in gn: fx2.add(n) gx = g.copy() gx.remove_node(v) if k_set: new_k1 = k-2 new_k2 = k-1 try: cyc.find_cycle(gx.subgraph(fx2)) mif_set1 = None except: mif_set1 = mif_preprocess_1(gx, fx2, t, new_k1) mif_set2 = mif_preprocess_1(g, fx1, t, new_k2) if not mif_set1: return mif_set2 elif not mif_set2: return mif_set1 else: return max(mif_set1, mif_set2, key=len) return None
def fvs_via_mif(g: MultiGraph, k: int) -> set: mif_set = mif(g, g.order()-k) if mif_set: nodes = set(g.nodes()) mif_set = nodes.difference(mif_set) return mif_set
def mif_main(self, g: MultiGraph, f: set, t) -> set: if f == g.nodes(): return f if not f: g_degree = g.degree() g_max_degree_node = max(g_degree, key=lambda n: n[1])[0] if g_degree[g_max_degree_node] <= 1: return set(g.nodes()) else: fx = f.copy() fx.add(g_max_degree_node) gx = g.copy() gx.remove_node(g_max_degree_node) mif_set1 = self.preprocess_1(g, fx, t) mif_set2 = self.preprocess_1(gx, f, t) if not mif_set1: return mif_set2 elif not mif_set2: return mif_set1 else: return max(mif_set1, mif_set2, key=len) # Set t as active vertex if t is None or t not in f: t = next(iter(f)) gd_over_3 = None gd_2 = None for v in g.neighbors(t): (gd_v, gn_v) = self.generalized_degree(g, f, t, v) if gd_v <= 1: f.add(v) return self.preprocess_1(g, f, t) elif gd_v >= 3: gd_over_3 = v else: gd_2 = (v, gn_v) if gd_over_3 is not None: # Cannot simply use "if gd_over_3" because v might be 0 fx = f.copy() fx.add(gd_over_3) gx = g.copy() gx.remove_node(gd_over_3) mif_set1 = self.preprocess_1(g, fx, t) mif_set2 = self.preprocess_1(gx, f, t) if not mif_set1: return mif_set2 elif not mif_set2: return mif_set1 else: return max(mif_set1, mif_set2, key=len) elif gd_2 is not None: (v, gn) = gd_2 fx1 = f.copy() fx2 = f.copy() fx1.add(v) for n in gn: fx2.add(n) gx = g.copy() gx.remove_node(v) try: find_cycle(gx.subgraph(fx2)) mif_set1 = None except: mif_set1 = self.preprocess_1(gx, fx2, t) mif_set2 = self.preprocess_1(g, fx1, t) if not mif_set1: return mif_set2 elif not mif_set2: return mif_set1 else: return max(mif_set1, mif_set2, key=len) return None
def get_matching_pairs_in_bipartite_graph(self, Gu: nx.MultiGraph, ls, rs, obj_node_id='obj') -> ( List[Tuple], List): """ Compares source and target nodes by labels and values and returns the matching pairs, or unmatched pairs if there are source nodes that can't be matched. N.b. the presence of a single unmatched Gs node suffices to deem the Gu as unmatched :param Gu: :param ls: :param rs: :param obj_node_id: identifier for the head node :return: """ NDV = Gu.nodes(data=True) # Compare and connect nodes in left partition and right partition # N.b. there could be zero connections in case of mismatch (False Caption for e.g.) is_head_node = lambda x: obj_node_id in x Gs_head_nodes = sorted(list(filter(is_head_node, ls))) Gt_head_nodes = sorted(list(filter(is_head_node, rs))) logger.debug(f'Number of head nodes in Gs (text graph) = {len(Gs_head_nodes)}') logger.debug(f'Number of head nodes in Gt (image graph) = {len(Gt_head_nodes)}') matching_pairs = [] # Holds the (s,t) matching pairs unmatched_pairs = [] # Holds the (s) source nodes that did not match for gs_head_node in Gs_head_nodes: print(f'Matching source head node: {NDV[gs_head_node]}') gs_hn_val = NDV[gs_head_node]['val'] gs_hn_graph, gs_hn_doc = self.clevr_parser.parse(gs_hn_val) gs_hn_span = gs_hn_doc.ents[0] matching_grounding_node = None # Try matching the src head node to one of the available grounding objects for gt_head_node in Gt_head_nodes: # gt = nx.subgraph(Gt, gt_head_node) # is_equal = __is_equal_head_nodes(gs, gt) gt_hn_val = NDV[gt_head_node]['val'] if gs_hn_val == gt_hn_val: matching_grounding_node = gt_head_node break gt_hn_graph, gt_hn_doc = self.clevr_parser.parse(gt_hn_val) gt_hn_span = gt_hn_doc.ents[0] # Compare <Z> attr1 = gs_hn_span._.size if gs_hn_span._.has_size else None attr2 = gt_hn_span._.size if attr1 is not None and len(attr1) > 0: _is_equal_attr = self.clevr_parser.entity_recognizer.is_equal_size(attr1, attr2) if not _is_equal_attr: continue # Compare <C> attr1 = gs_hn_span._.color if gs_hn_span._.has_color else None attr2 = gt_hn_span._.color if attr1 and (attr1.text != attr2.text): # Color is stipulated for source, but doesn't match target continue # Compare <M> attr1 = gs_hn_span._.material if gs_hn_span._.has_material else None attr2 = gt_hn_span._.material if attr1 is not None and len(attr1) > 0: _is_equal_attr = self.clevr_parser.entity_recognizer.is_equal_material(attr1, attr2) if not _is_equal_attr: continue # Compare <S> attr1 = gs_hn_span._.shape if gs_hn_span._.has_shape else None attr2 = gt_hn_span._.shape if attr1 is not None and len(attr1) > 0: _is_equal_attr = self.clevr_parser.entity_recognizer.is_equal_shape(attr1, attr2) if not _is_equal_attr: continue # Found Grounding Node Match matching_grounding_node = gt_head_node break if matching_grounding_node: matching_pairs.append((gs_head_node, matching_grounding_node)) else: unmatched_pairs.append(gs_head_node) logger.info(f'\tNumber of matching pairs (gs,gt) found = {len(matching_pairs)}' f'\n\tNumber of unmatched pairs (gs) found = {len(unmatched_pairs)}') logger.info(f'matching_pairs = {matching_pairs}') return matching_pairs, unmatched_pairs
def get_embeddings(self, G: nx.MultiGraph, doc, embd_dim=96, embedding_type=None, **kwargs) -> np.ndarray: """ Example: Text: "There is a green metal block; the tiny metal thing is to the left of it" Gs -> ['obj', '<C>', '<M>', '<S>', 'obj2', '<Z2>', '<M2>', '<S2>'] doc.ents -> (green metal block, tiny metal thing) NodeDataView({'obj': {'label': 'CLEVR_OBJ', 'val': 'green metal block'}, '<C>': {'label': 'color', 'val': 'green'}, '<M>': {'label': 'material', 'val': 'metal'}, '<S>': {'label': 'shape', 'val': 'block'}, 'obj2': {'label': 'CLEVR_OBJ', 'val': 'tiny metal thing'}, '<Z2>': {'label': 'size', 'val': 'tiny'}, '<M2>': {'label': 'material', 'val': 'metal'}, '<S2>': {'label': 'shape', 'val': 'thing'}}) :param G: MultiGraph containing all CLEVR nodes :param doc: Spacy Doc :param embd_dim: :param embedding_type: :return: A feature vector matrix corresponding to the value of the Graph of size (N * M) where N is the number of nodes in the graph and M corresopnds to the embd_sz (default = 96) Note: the head_node ('obj' node) will be a mean of all attrs vecs (of embd_sz) """ import warnings warnings.warn("Deprecated: use `get_node_feature_matrix` instead", DeprecationWarning, stacklevel=2) assert G is not None NDV = G.nodes(data=True) NV = G.nodes(data=False) _is_head_node = lambda x: 'obj' in x head_nodes = list(filter(_is_head_node, NV)) objs = self.clevr_parser.filter_clevr_objs(doc.ents) N = len(NDV) M = embd_dim feat_mats = [] for i, entity in enumerate(objs): if entity.label_ not in ('CLEVR_OBJS', 'CLEVR_OBJ'): continue ent_mat = self.clevr_parser.get_clevr_entity_matrix_embedding(entity, dim=96, include_obj_node_emd=True) feat_mats.append(ent_mat) head_node = G.nodes.get(head_nodes[i]) pos = head_node.get('pos') # pos = (x,y,z): Tuple[float] if len(feat_mats) > 1: feat_mats = reduce(lambda a, b: np.vstack((a, b)), feat_mats) else: feat_mats = feat_mats[0] assert feat_mats.shape == (N, M) spatial_ents = self.clevr_parser.filter_spatial_re(doc.ents) for i, entity in enumerate(spatial_ents): ent_vec = entity.vector.reshape(1, -1) #(1, 96) feat_mats = np.vstack((feat_mats, ent_vec)) matching_ents = self.clevr_parser.filter_matching_re(doc.ents) for i, entity in enumerate(matching_ents): ent_vec = entity.vector.reshape(1, -1) # (1, 96) feat_mats = np.vstack((feat_mats, ent_vec)) return feat_mats