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