def testDigraphEmptyGraph(self): g = digraph() f = g.clone() for x in g, f: self.assertEqual(bool(x), False) self.assertEqual(x.contains("A"), False) self.assertEqual(x.firstzero(), None) self.assertRaises(KeyError, x.remove, "A") x.delnode("A") self.assertEqual(list(x), []) self.assertEqual(x.get("A"), None) self.assertEqual(x.get("A", "default"), "default") self.assertEqual(x.all_nodes(), []) self.assertEqual(x.leaf_nodes(), []) self.assertEqual(x.root_nodes(), []) self.assertRaises(KeyError, x.child_nodes, "A") self.assertRaises(KeyError, x.parent_nodes, "A") self.assertEqual(x.hasallzeros(), True) self.assertRaises(KeyError, list, x.bfs("A")) self.assertRaises(KeyError, x.shortest_path, "A", "B") self.assertRaises(KeyError, x.remove_edge, "A", "B") self.assertEqual(x.get_cycles(), []) x.difference_update("A") portage.util.noiselimit = -2 x.debug_print() portage.util.noiselimit = 0
def testDigraphIgnorePriority(self): def always_true(dummy): return True def always_false(dummy): return False g = digraph() g.add("A", "B") self.assertEqual(g.parent_nodes("A"), ["B"]) self.assertEqual(g.parent_nodes("A", ignore_priority=always_false), ["B"]) self.assertEqual(g.parent_nodes("A", ignore_priority=always_true), []) self.assertEqual(g.child_nodes("B"), ["A"]) self.assertEqual(g.child_nodes("B", ignore_priority=always_false), ["A"]) self.assertEqual(g.child_nodes("B", ignore_priority=always_true), []) self.assertEqual(g.leaf_nodes(), ["A"]) self.assertEqual(g.leaf_nodes(ignore_priority=always_false), ["A"]) self.assertEqual(g.leaf_nodes(ignore_priority=always_true), ["A", "B"]) self.assertEqual(g.root_nodes(), ["B"]) self.assertEqual(g.root_nodes(ignore_priority=always_false), ["B"]) self.assertEqual(g.root_nodes(ignore_priority=always_true), ["A", "B"])
def testDigraphCircle(self): g = digraph() g.add("A", "B", -1) g.add("B", "C", 0) g.add("C", "D", 1) g.add("D", "A", 2) f = g.clone() h = digraph() h.update(f) for x in g, f, h: self.assertEqual(bool(x), True) self.assertEqual(x.contains("A"), True) self.assertEqual(x.firstzero(), None) self.assertRaises(KeyError, x.remove, "Z") x.delnode("Z") self.assertEqual(list(x), ["A", "B", "C", "D"]) self.assertEqual(x.get("A"), "A") self.assertEqual(x.get("A", "default"), "A") self.assertEqual(x.all_nodes(), ["A", "B", "C", "D"]) self.assertEqual(x.leaf_nodes(), []) self.assertEqual(x.root_nodes(), []) self.assertEqual(x.child_nodes("A"), ["D"]) self.assertEqual(x.child_nodes("A", ignore_priority=2), []) self.assertEqual(x.parent_nodes("A"), ["B"]) self.assertEqual(x.parent_nodes("A", ignore_priority=-2), ["B"]) self.assertEqual(x.parent_nodes("A", ignore_priority=-1), []) self.assertEqual(x.hasallzeros(), False) self._assertBFSEqual(x.bfs("A"), [(None, "A"), ("A", "D"), ("D", "C"), ("C", "B")]) self.assertEqual(x.shortest_path("A", "D"), ["A", "D"]) self.assertEqual(x.shortest_path("D", "A"), ["D", "C", "B", "A"]) self.assertEqual(x.shortest_path("A", "D", ignore_priority=2), None) self.assertEqual(x.shortest_path("D", "A", ignore_priority=-2), ["D", "C", "B", "A"]) cycles = set(tuple(y) for y in x.get_cycles()) self.assertEqual(cycles, set([("D", "C", "B", "A"), ("C", "B", "A", "D"), ("B", "A", "D", "C"), \ ("A", "D", "C", "B")])) x.remove_edge("A", "B") self.assertEqual(x.get_cycles(), []) x.difference_update(["D"]) self.assertEqual(x.all_nodes(), ["A", "B", "C"]) portage.util.noiselimit = -2 x.debug_print() portage.util.noiselimit = 0
def testBackwardCompatibility(self): g = digraph() f = g.copy() g.addnode("A", None) self.assertEqual("A" in g, True) self.assertEqual(bool(g), True) self.assertEqual(g.allnodes(), ["A"]) self.assertEqual(g.allzeros(), ["A"]) self.assertEqual(g.hasnode("A"), True)
def testDigraphTree(self): g = digraph() g.add("B", "A", -1) g.add("C", "A", 0) g.add("D", "C", 1) g.add("E", "C", 2) f = g.clone() for x in g, f: self.assertEqual(x.is_empty(), False) self.assertEqual(x.contains("A"), True) self.assertEqual(x.firstzero(), "B") self.assertRaises(KeyError, x.remove, "Z") x.delnode("Z") self.assertEqual(set(x), set(["A", "B", "C", "D", "E"])) self.assertEqual(x.get("A"), "A") self.assertEqual(x.get("A", "default"), "A") self.assertEqual(set(x.all_nodes()), set(["A", "B", "C", "D", "E"])) self.assertEqual(set(x.leaf_nodes()), set(["B", "D", "E"])) self.assertEqual(set(x.leaf_nodes(ignore_priority=0)), set(["A", "B", "D", "E"])) self.assertEqual(x.root_nodes(), ["A"]) self.assertEqual(set(x.root_nodes(ignore_priority=0)), set(["A", "B", "C"])) self.assertEqual(set(x.child_nodes("A")), set(["B", "C"])) self.assertEqual(x.child_nodes("A", ignore_priority=2), []) self.assertEqual(x.parent_nodes("B"), ["A"]) self.assertEqual(x.parent_nodes("B", ignore_priority=-2), ["A"]) self.assertEqual(x.parent_nodes("B", ignore_priority=-1), []) self.assertEqual(x.hasallzeros(), False) self.assertEqual(list(x.bfs("A")), [(None, "A"), ("A", "C"), ("A", "B"), ("C", "E"), ("C", "D")]) self.assertEqual(x.shortest_path("A", "D"), ["A", "C", "D"]) self.assertEqual(x.shortest_path("D", "A"), None) self.assertEqual(x.shortest_path("A", "D", ignore_priority=2), None) cycles = set(tuple(y) for y in x.get_cycles()) self.assertEqual(cycles, set()) x.remove("D") self.assertEqual(set(x.all_nodes()), set(["A", "B", "C", "E"])) x.remove("C") self.assertEqual(set(x.all_nodes()), set(["A", "B", "E"])) portage.util.noiselimit = -2 x.debug_print() portage.util.noiselimit = 0 self.assertRaises(KeyError, x.remove_edge, "A", "E")
def _init_graph(self): ''' Graph relationships between repos and their masters. ''' self._sync_graph = digraph() self._leaf_nodes = [] self._repo_map = {} self._running_repos = set() selected_repo_names = frozenset(repo.name for repo in self._selected_repos) for repo in self._selected_repos: self._repo_map[repo.name] = repo self._sync_graph.add(repo.name, None) for master in repo.masters: if master.name in selected_repo_names: self._repo_map[master.name] = master self._sync_graph.add(master.name, repo.name) self._update_leaf_nodes()
def _overlap_dnf(dep_struct): """ Combine overlapping || groups using disjunctive normal form (DNF), in order to minimize the number of packages chosen to satisfy cases like "|| ( foo bar ) || ( bar baz )" as in bug #632026. Non-overlapping groups are excluded from the conversion, since DNF leads to exponential explosion of the formula. When dep_struct does not contain any overlapping groups, no DNF conversion will be performed, and dep_struct will be returned as-is. Callers can detect this case by checking if the returned object has the same identity as dep_struct. If the identity is different, then DNF conversion was performed. """ if not _contains_disjunction(dep_struct): return dep_struct # map atom.cp to disjunctions cp_map = collections.defaultdict(list) # graph atom.cp, with edges connecting atoms in the same disjunction overlap_graph = digraph() # map id(disjunction) to index in dep_struct, for deterministic output order_map = {} order_key = lambda x: order_map[id(x)] result = [] for i, x in enumerate(dep_struct): if isinstance(x, list): assert x and x[0] == '||', \ 'Normalization error, nested conjunction found in %s' % (dep_struct,) order_map[id(x)] = i prev_cp = None for atom in _iter_flatten(x): if isinstance(atom, Atom) and not atom.blocker: cp_map[atom.cp].append(x) overlap_graph.add(atom.cp, parent=prev_cp) prev_cp = atom.cp if prev_cp is None: # only contains blockers result.append(x) else: result.append(x) # group together disjunctions having atom.cp overlap traversed = set() overlap = False for cp in overlap_graph: if cp in traversed: continue disjunctions = {} stack = [cp] while stack: cp = stack.pop() traversed.add(cp) for x in cp_map[cp]: disjunctions[id(x)] = x for other_cp in itertools.chain(overlap_graph.child_nodes(cp), overlap_graph.parent_nodes(cp)): if other_cp not in traversed: stack.append(other_cp) if len(disjunctions) > 1: overlap = True # convert overlapping disjunctions to DNF result.extend( _dnf_convert(sorted(disjunctions.values(), key=order_key))) else: # pass through non-overlapping disjunctions result.append(disjunctions.popitem()[1]) return result if overlap else dep_struct
def _overlap_dnf(dep_struct): """ Combine overlapping || groups using disjunctive normal form (DNF), in order to minimize the number of packages chosen to satisfy cases like "|| ( foo bar ) || ( bar baz )" as in bug #632026. Non-overlapping groups are excluded from the conversion, since DNF leads to exponential explosion of the formula. When dep_struct does not contain any overlapping groups, no DNF conversion will be performed, and dep_struct will be returned as-is. Callers can detect this case by checking if the returned object has the same identity as dep_struct. If the identity is different, then DNF conversion was performed. """ if not _contains_disjunction(dep_struct): return dep_struct # map atom.cp to disjunctions cp_map = collections.defaultdict(list) # graph atom.cp, with edges connecting atoms in the same disjunction overlap_graph = digraph() # map id(disjunction) to index in dep_struct, for deterministic output order_map = {} order_key = lambda x: order_map[id(x)] result = [] for i, x in enumerate(dep_struct): if isinstance(x, list): assert x and x[0] == '||', \ 'Normalization error, nested conjunction found in %s' % (dep_struct,) order_map[id(x)] = i prev_cp = None for atom in _iter_flatten(x): if isinstance(atom, Atom) and not atom.blocker: cp_map[atom.cp].append(x) overlap_graph.add(atom.cp, parent=prev_cp) prev_cp = atom.cp if prev_cp is None: # only contains blockers result.append(x) else: result.append(x) # group together disjunctions having atom.cp overlap traversed = set() overlap = False for cp in overlap_graph: if cp in traversed: continue disjunctions = {} stack = [cp] while stack: cp = stack.pop() traversed.add(cp) for x in cp_map[cp]: disjunctions[id(x)] = x for other_cp in itertools.chain(overlap_graph.child_nodes(cp), overlap_graph.parent_nodes(cp)): if other_cp not in traversed: stack.append(other_cp) if len(disjunctions) > 1: overlap = True # convert overlapping disjunctions to DNF result.extend(_dnf_convert( sorted(disjunctions.values(), key=order_key))) else: # pass through non-overlapping disjunctions result.append(disjunctions.popitem()[1]) return result if overlap else dep_struct