def __init__(self, nodes): self.cid = 0 self.judge = Judge(nodes) edges = self.judge.get_conflicts() self.dotgraph = nx.DiGraph() self.graph_container = Gexf("Nico Rotstein", "Arguiew (reviews as argumentation) graph") self.dotnodes = {} self.has_compressed = {} self.warranted = set([]) self.redundant = {} for n in nodes: if n.attributes != []: self.dotgraph.add_node(n.id, shape="record", label=n.get_label()) self.dotnodes[n.id] = n for (n1, n2) in edges: if self.judge.equivalent(n1, n2): self.dotgraph.add_edge(n1.id, n2.id, color="red", dir="both", label=n1.get_conf_label(n2)) self.dotgraph.add_edge(n2.id, n1.id, color="transparent") else: (better, worse) = self.judge.get_better_review(n1, n2) self.dotgraph.add_edge(better.id, worse.id, label=better.get_conf_label(worse)) self.prettyGraph()
class Grapher: """ handles all things related to graph handling it keeps the graph and a dictionary of nodes whose key is the node id and the value is the node itself """ def __init__(self, nodes): self.cid = 0 self.judge = Judge(nodes) edges = self.judge.get_conflicts() self.dotgraph = nx.DiGraph() self.graph_container = Gexf("Nico Rotstein", "Arguiew (reviews as argumentation) graph") self.dotnodes = {} self.has_compressed = {} self.warranted = set([]) for n in nodes: if n.attributes != []: self.dotgraph.add_node(n.id, shape="record", label=n.get_label()) self.dotnodes[n.id] = n for (n1, n2) in edges: if self.judge.equivalent(n1, n2): self.dotgraph.add_edge(n1.id, n2.id, color="red", dir="both", label=n1.get_conf_label(n2)) self.dotgraph.add_edge(n2.id, n1.id, color="transparent") else: (better, worse) = self.judge.get_better_review(n1, n2) self.dotgraph.add_edge(better.id, worse.id, label=better.get_conf_label(worse)) self.prettyGraph() def get_container(self): graph = self.graph_container.addGraph("directed", "static", "Arguiew graph") attWarrant = graph.addNodeAttribute("warranted", "false", "boolean") attPosText = graph.addNodeAttribute("positive_text", "", "string") attNegText = graph.addNodeAttribute("negative_text", "", "string") attHasCompressed = graph.addNodeAttribute("has_compressed", "[]", "liststring") nodes = [] for n in self.dotgraph.nodes(): nodes.append(self.dotnodes[n]) self.judge = Judge(nodes) edges = self.judge.get_conflicts() node_handlers = {} for n in nodes: i = graph.addNode(n.id, n.get_formatted_atts()) node_handlers[n.id] = i i.addAttribute(attPosText, str(n.positive_text)) i.addAttribute(attNegText, str(n.negative_text)) if self.has_compressed != {}: i.addAttribute(attHasCompressed, str(list(self.has_compressed[n.id]))) for (n1, n2) in edges: graph.addEdge(n1.id + "-" + n2.id, n1.id, n2.id, label=n1.get_conf_label(n2)) for w in self.warranted: node_handlers[w].addAttribute(attWarrant, "true") node_handlers[w].setColor("20", "200", "20") return self.graph_container def get_dotgraph(self): return self.dotgraph def resolve_cycles(self): # capture all cycles cycles = nx.simple_cycles(self.dotgraph) # the kind of cycles we are interested in are either binary (eg, [1,2,1]) or # even-lengthed (eg, [0,1,2,0]) - here we capture the latter and flag them # as "blocked"- don't be fooled by the length of the list blocked = [] for cycle in cycles: # if (len(cycle) % 2 == 0): blocked += cycle affected_by_cycles = [] preds = {} succs = {} for cycle in cycles: # we store all predecessors of all cycles and compute all successors cycle_set = list(set(cycle)) cycle_predecessors = tools.all_predecessors(self.dotgraph, cycle_set, []) cycle_predecessors = list(set(cycle_predecessors) - set(blocked)) cycle_successors = tools.all_successors(self.dotgraph, cycle_set, []) preds[str(cycle)] = cycle_predecessors # whenever a cycle has no predecessors, it introduces undecidedness # hence we store all successors in a global list of nodes that are # "affected by cycles" if cycle_predecessors == []: affected_by_cycles += cycle_successors # those cycles that have no predecessors or that their predecessors # are affected by cycles that have no predecessors or... and so on and so forth out = [] for cycle in cycles: if (preds[str(cycle)] == []) or (set(preds[str(cycle)]) & set(affected_by_cycles) == []): out += cycle if out != []: # print "out due to unforgiving cycles: ", out print len(out), "reviews were involved in improper cycling" for o in set(out): self.dotgraph.remove_node(o) def prettyGraph(self): self.remove_dupes() print "resolving cycles..." self.resolve_cycles() print "computing accepted reviews..." self.set_warranted() def compress(self): print "compressing graph..." self.compress() print "removing redundant reviews in compressed graph" self.remove_dupes() def remove_dupes(self): removals = [] remaining_nodes = set(self.dotgraph.nodes()) while remaining_nodes != set([]): n = remaining_nodes.pop() for n2 in remaining_nodes: if set(self.dotnodes[n2].attributes).issubset(set(self.dotnodes[n].attributes)): removals.append(n2) if removals != []: if len(set(removals)) == 1: print "1 review was redundant" else: print len(set(removals)), "reviews were redundant" for r in set(removals): print "review " + r + " was redundant - should be attached to the review that stayed in!!!" self.dotgraph.remove_node(r) def set_warranted(self): undefeated = set([node for (node, x) in self.dotgraph.edges()]) - set( [node for (x, node) in self.dotgraph.edges()] ) undefeated |= set([node for node in self.dotgraph.nodes() if nx.is_isolate(self.dotgraph, node)]) warranted = undefeated | self.judge.grounded(undefeated, self.dotgraph, set([]), set([])) for w in warranted: self.dotgraph.add_node(w, style="filled", fillcolor="green") self.warranted = warranted print len(warranted), "reviews were accepted" def compress(self): first_stage = set([node for (node, x) in self.dotgraph.edges()]) - set( [node for (x, node) in self.dotgraph.edges()] ) first_stage |= set([node for node in self.dotgraph.nodes() if nx.is_isolate(self.dotgraph, node)]) defeat_stages = [first_stage] + self.stages(first_stage, first_stage) cs = [] for stage in defeat_stages: cs += self.consistent_subsets(stage, self.warranted) compressed_dotnodes = {} has_compressed = {} compressed_warranted = set([]) compressed_dotgraph = nx.DiGraph() for subset in cs: positive_feats = set([]) negative_feats = set([]) for i in subset: positive_feats |= set(self.dotnodes[i].get_positive_feats()) negative_feats |= set(self.dotnodes[i].get_negative_feats()) r = Review( "c" + str(self.cid), {"feats": list(positive_feats), "text": ""}, {"feats": list(negative_feats), "text": ""}, ) compressed_dotnodes[r.id] = r has_compressed[r.id] = subset self.cid += 1 if subset.issubset(self.warranted): compressed_warranted.add(r.id) compressed_dotgraph.add_node( r.id, style="filled", fillcolor="green", shape="record", label=str(r.subset_label(subset)) ) else: compressed_dotgraph.add_node(r.id, shape="record", label=str(r.subset_label(subset))) for id1, n1 in has_compressed.items(): for id2, n2 in has_compressed.items(): for i in n1: for j in n2: ri = self.dotnodes[i] rj = self.dotnodes[j] if ( ri.in_conflict(rj) and not (id1, id2) in compressed_dotgraph.edges() and not (id2, id1) in compressed_dotgraph.edges() ): compressed_dotgraph.add_edge(id1, id2, dir="none") self.warranted = compressed_warranted self.dotgraph = compressed_dotgraph self.dotnodes = compressed_dotnodes self.has_compressed = has_compressed def stages(self, fringe, so_far): next = set() for r in fringe: next = next | set(self.dotgraph.successors(r)) next = next - so_far - fringe if next == set([]): return [] else: return [next] + self.stages(next, so_far | fringe) def consistent_subsets(self, stage, warranted): elem = stage.pop() (incons, consis) = self.consistent_in_rest([elem], stage, warranted) if incons == set([]): return [consis] else: return [consis] + self.consistent_subsets(incons, warranted) def consistent_in_rest(self, elems, rest, warranted): if rest == set([]): return (set([]), set(elems)) else: ss = set() ps = set() for elem in elems: ss |= set(self.dotgraph.successors(elem)) ps |= set(self.dotgraph.predecessors(elem)) next = rest.pop() if next in ss or next in ps or set(elems).issubset(warranted) and next not in warranted: (i, c) = self.consistent_in_rest(elems, rest, warranted) return (i | set([next]), c | set(elems)) else: (i, c) = self.consistent_in_rest(elems + [next], rest, warranted) return (i, set(elems + [next]) | c)