mirror of
				https://git.launchpad.net/~ubuntu-release/britney/+git/britney2-ubuntu
				synced 2025-11-04 02:24:24 +00:00 
			
		
		
		
	solver: Extract compute_scc into a function
Signed-off-by: Niels Thykier <niels@thykier.net>
This commit is contained in:
		
							parent
							
								
									bd375fdd85
								
							
						
					
					
						commit
						69473eefca
					
				@ -21,6 +21,115 @@ from britney2.utils import (ifilter_only, iter_except)
 | 
			
		||||
from britney2.installability.tester import InstallabilityTester
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def compute_scc(graph):
 | 
			
		||||
    """Iterative algorithm for strongly-connected components
 | 
			
		||||
 | 
			
		||||
    Iterative variant of Tarjan's algorithm for finding strongly-connected
 | 
			
		||||
    components.
 | 
			
		||||
 | 
			
		||||
    :param graph: Table of all nodes along which their edges (in "before" and "after")
 | 
			
		||||
    :return: List of components (each component is a list of items)
 | 
			
		||||
    """
 | 
			
		||||
    result = []
 | 
			
		||||
    low = {}
 | 
			
		||||
    node_stack = []
 | 
			
		||||
 | 
			
		||||
    def _handle_succ(parent, parent_num, successors_remaining):
 | 
			
		||||
        while successors_remaining:
 | 
			
		||||
            succ = successors_remaining.pop()
 | 
			
		||||
            succ_num = low.get(succ, None)
 | 
			
		||||
            if succ_num is not None:
 | 
			
		||||
                if succ_num < parent_num:
 | 
			
		||||
                    # These two nodes are part of the probably
 | 
			
		||||
                    # same SSC (or succ is isolated
 | 
			
		||||
                    low[parent] = parent_num = succ_num
 | 
			
		||||
                continue
 | 
			
		||||
            # It cannot be a part of a SCC if it does not have depends
 | 
			
		||||
            # or reverse depends.
 | 
			
		||||
            if not graph[succ]['before'] or not graph[succ]['after']:
 | 
			
		||||
                # Short-cut obviously isolated component
 | 
			
		||||
                result.append((succ,))
 | 
			
		||||
                # Set the item number so high that no other item might
 | 
			
		||||
                # mistakenly assume that they can form a component via
 | 
			
		||||
                # this item.
 | 
			
		||||
                # (Replaces the "is w on the stack check" for us from
 | 
			
		||||
                #  the original algorithm)
 | 
			
		||||
                low[succ] = len(graph) + 1
 | 
			
		||||
                continue
 | 
			
		||||
            succ_num = len(low)
 | 
			
		||||
            low[succ] = succ_num
 | 
			
		||||
            work_stack.append((succ, len(node_stack), succ_num, graph[succ]['before']))
 | 
			
		||||
            node_stack.append(succ)
 | 
			
		||||
            # "Recurse" into the child node first
 | 
			
		||||
            return True
 | 
			
		||||
        return False
 | 
			
		||||
 | 
			
		||||
    for n in graph:
 | 
			
		||||
        if n in low:
 | 
			
		||||
            continue
 | 
			
		||||
        # It cannot be a part of a SCC if it does not have depends
 | 
			
		||||
        # or reverse depends.
 | 
			
		||||
        if not graph[n]['before'] or not graph[n]['after']:
 | 
			
		||||
            # Short-cut obviously isolated component
 | 
			
		||||
            result.append((n,))
 | 
			
		||||
            # Set the item number so high that no other item might
 | 
			
		||||
            # mistakenly assume that they can form a component via
 | 
			
		||||
            # this item.
 | 
			
		||||
            # (Replaces the "is w on the stack check" for us from
 | 
			
		||||
            #  the original algorithm)
 | 
			
		||||
            low[n] = len(graph) + 1
 | 
			
		||||
            continue
 | 
			
		||||
 | 
			
		||||
        root_num = len(low)
 | 
			
		||||
        low[n] = root_num
 | 
			
		||||
        # DFS work-stack needed to avoid call recursion.  It (more or less)
 | 
			
		||||
        # replaces the variables on the call stack in Tarjan's algorithm
 | 
			
		||||
        work_stack = [(n, len(node_stack), root_num, graph[n]['before'])]
 | 
			
		||||
        node_stack.append(n)
 | 
			
		||||
        while work_stack:
 | 
			
		||||
            node, stack_idx, orig_node_num, successors = work_stack[-1]
 | 
			
		||||
            if successors and _handle_succ(node, low[node], successors):
 | 
			
		||||
                # _handle_succ has pushed a new node on to work_stack
 | 
			
		||||
                # and we need to "restart" the loop to handle that first
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            # This node is done; remove it from the work stack
 | 
			
		||||
            work_stack.pop()
 | 
			
		||||
 | 
			
		||||
            # This node is out of successor.  Push up the "low" value
 | 
			
		||||
            # (Exception: root node has no parent)
 | 
			
		||||
            node_num = low[node]
 | 
			
		||||
            if work_stack:
 | 
			
		||||
                parent = work_stack[-1][0]
 | 
			
		||||
                parent_num = low[parent]
 | 
			
		||||
                if node_num <= parent_num:
 | 
			
		||||
                    # This node is a part of a component with its parent.
 | 
			
		||||
                    # We update the parent's node number and push the
 | 
			
		||||
                    # responsibility of building the component unto the
 | 
			
		||||
                    # parent.
 | 
			
		||||
                    low[parent] = node_num
 | 
			
		||||
                    continue
 | 
			
		||||
                if node_num != orig_node_num:
 | 
			
		||||
                    # The node is a part of an SCC with a ancestor (and parent)
 | 
			
		||||
                    continue
 | 
			
		||||
            # We got a component
 | 
			
		||||
            component = tuple(node_stack[stack_idx:])
 | 
			
		||||
            del node_stack[stack_idx:]
 | 
			
		||||
            result.append(component)
 | 
			
		||||
            # Re-number all items, so no other item might
 | 
			
		||||
            # mistakenly assume that they can form a component via
 | 
			
		||||
            # one of these items.
 | 
			
		||||
            # (Replaces the "is w on the stack check" for us from
 | 
			
		||||
            #  the original algorithm)
 | 
			
		||||
            new_num = len(graph) + 1
 | 
			
		||||
            for item in component:
 | 
			
		||||
                low[item] = new_num
 | 
			
		||||
 | 
			
		||||
    assert not node_stack
 | 
			
		||||
 | 
			
		||||
    return result
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class InstallabilitySolver(InstallabilityTester):
 | 
			
		||||
 | 
			
		||||
    def __init__(self, universe, revuniverse, testing, broken, essentials,
 | 
			
		||||
@ -182,7 +291,7 @@ class InstallabilitySolver(InstallabilityTester):
 | 
			
		||||
        #
 | 
			
		||||
        # Each one of those components will become an "easy" hint.
 | 
			
		||||
 | 
			
		||||
        comps = self._compute_scc(order)
 | 
			
		||||
        comps = compute_scc(order)
 | 
			
		||||
        merged = {}
 | 
			
		||||
        scc = {}
 | 
			
		||||
        # Now that we got the SSCs (in comps), we select on item from
 | 
			
		||||
@ -269,93 +378,6 @@ class InstallabilitySolver(InstallabilityTester):
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def _compute_scc(self, order):
 | 
			
		||||
        """Iterative algorithm for strongly-connected components
 | 
			
		||||
 | 
			
		||||
        Iterative variant of Tarjan's algorithm for finding strongly-connected
 | 
			
		||||
        components.
 | 
			
		||||
 | 
			
		||||
        :param order: Table of all nodes along which their ordering constraints
 | 
			
		||||
        :return: List of components (each component is a list of items)
 | 
			
		||||
        """
 | 
			
		||||
        result = []
 | 
			
		||||
        low = {}
 | 
			
		||||
        node_stack = []
 | 
			
		||||
 | 
			
		||||
        def _handle_succ(parent, parent_num, successors_remaining):
 | 
			
		||||
            while successors_remaining:
 | 
			
		||||
                succ = successors_remaining.pop()
 | 
			
		||||
                succ_num = low.get(succ, None)
 | 
			
		||||
                if succ_num is not None:
 | 
			
		||||
                    if succ_num < parent_num:
 | 
			
		||||
                        low[parent] = parent_num = succ_num
 | 
			
		||||
                    continue
 | 
			
		||||
                succ_num = len(low)
 | 
			
		||||
                low[succ] = succ_num
 | 
			
		||||
                # It cannot be a part of a SCC if it does not have depends
 | 
			
		||||
                # or reverse depends.
 | 
			
		||||
                if not order[succ]['before'] or not order[succ]['after']:
 | 
			
		||||
                    # Short-cut obviously isolated component
 | 
			
		||||
                    result.append((succ,))
 | 
			
		||||
                    continue
 | 
			
		||||
                work_stack.append((succ, len(node_stack), succ_num, order[succ]['before']))
 | 
			
		||||
                node_stack.append(succ)
 | 
			
		||||
                # "Recurse" into the child node first
 | 
			
		||||
                return True
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        for n in order:
 | 
			
		||||
            if n in low:
 | 
			
		||||
                continue
 | 
			
		||||
            root_num = len(low)
 | 
			
		||||
            low[n] = root_num
 | 
			
		||||
            # It cannot be a part of a SCC if it does not have depends
 | 
			
		||||
            # or reverse depends.
 | 
			
		||||
            if not order[n]['before'] or not order[n]['after']:
 | 
			
		||||
                # Short-cut obviously isolated component
 | 
			
		||||
                result.append((n,))
 | 
			
		||||
                continue
 | 
			
		||||
            # DFS work-stack needed to avoid call recursion.  It (more or less)
 | 
			
		||||
            # replaces the variables on the call stack in Tarjan's algorithm
 | 
			
		||||
            work_stack = [(n, len(node_stack), root_num, order[n]['before'])]
 | 
			
		||||
            node_stack.append(n)
 | 
			
		||||
            while work_stack:
 | 
			
		||||
                node, stack_idx, orig_node_num, successors = work_stack[-1]
 | 
			
		||||
                if successors and _handle_succ(node, low[node], successors):
 | 
			
		||||
                    # _handle_succ has pushed a new node on to work_stack
 | 
			
		||||
                    # and we need to "restart" the loop to handle that first
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                # This node is done; remove it from the work stack
 | 
			
		||||
                work_stack.pop()
 | 
			
		||||
 | 
			
		||||
                # This node is out of successor.  Push up the "low" value
 | 
			
		||||
                # (Exception: root node has no parent)
 | 
			
		||||
                node_num = low[node]
 | 
			
		||||
                if work_stack:
 | 
			
		||||
                    parent = work_stack[-1][0]
 | 
			
		||||
                    parent_num = low[parent]
 | 
			
		||||
                    if node_num <= parent_num:
 | 
			
		||||
                        # This node is a part of a component with its parent.
 | 
			
		||||
                        # We update the parent's node number and push the
 | 
			
		||||
                        # responsibility of building the component unto the
 | 
			
		||||
                        # parent.
 | 
			
		||||
                        low[parent] = node_num
 | 
			
		||||
                        continue
 | 
			
		||||
                    if node_num != orig_node_num:
 | 
			
		||||
                        # The node is a part of an SCC with a ancestor (and parent)
 | 
			
		||||
                        continue
 | 
			
		||||
                # We got a component
 | 
			
		||||
                component = tuple(node_stack[stack_idx:])
 | 
			
		||||
                del node_stack[stack_idx:]
 | 
			
		||||
                result.append(component)
 | 
			
		||||
                for item in component:
 | 
			
		||||
                    low[item] = node_num
 | 
			
		||||
 | 
			
		||||
        assert not node_stack
 | 
			
		||||
 | 
			
		||||
        return result
 | 
			
		||||
 | 
			
		||||
    def _dump_groups(self, groups):  # pragma: no cover
 | 
			
		||||
        print("N: === Groups ===")
 | 
			
		||||
        for (item, adds, rms) in groups:
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user