Exemple #1
0
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
Exemple #2
0
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