Beispiel #1
0
def test_compute_topological_order():
    from pytools.graph import compute_topological_order, CycleError

    empty = {}
    assert compute_topological_order(empty) == []

    disconnected = {1: [], 2: [], 3: []}
    assert len(compute_topological_order(disconnected)) == 3

    line = list(zip(range(10), ([i] for i in range(1, 11))))
    import random
    random.seed(0)
    random.shuffle(line)
    expected = list(range(11))
    assert compute_topological_order(dict(line)) == expected

    claw = {1: [2, 3], 0: [1]}
    assert compute_topological_order(claw)[:2] == [0, 1]

    repeated_edges = {1: [2, 2], 2: [0]}
    assert compute_topological_order(repeated_edges) == [1, 2, 0]

    self_cycle = {1: [1]}
    with pytest.raises(CycleError):
        compute_topological_order(self_cycle)

    cycle = {0: [2], 1: [2], 2: [3], 3: [4, 1]}
    with pytest.raises(CycleError):
        compute_topological_order(cycle)
Beispiel #2
0
def test_prioritzed_topological_sort_examples():

    from pytools.graph import compute_topological_order

    keys = {"a": 4, "b": 3, "c": 2, "e": 1, "d": 4}
    dag = {"a": ["b", "c"], "b": [], "c": ["d", "e"], "d": [], "e": []}

    assert compute_topological_order(
        dag, key=keys.get) == ["a", "c", "e", "b", "d"]

    keys = {"a": 7, "b": 2, "c": 1, "d": 0}
    dag = {
        "d": set("c"),
        "b": set("a"),
        "a": set(),
        "c": set("a"),
    }

    assert compute_topological_order(dag, key=keys.get) == ["d", "c", "b", "a"]
Beispiel #3
0
    def make_partition(self, outputs: DictOfNamedArrays) -> GraphPartition:
        rewritten_outputs = {
                name: self(expr) for name, expr in outputs._data.items()}

        pid_to_output_names: Dict[PartId, Set[str]] = {
            pid: set() for pid in self.seen_part_ids}
        pid_to_input_names: Dict[PartId, Set[str]] = {
            pid: set() for pid in self.seen_part_ids}

        var_name_to_result = self.var_name_to_result.copy()

        for out_name, rewritten_output in rewritten_outputs.items():
            out_part_id = self._get_part_id(outputs._data[out_name])
            pid_to_output_names.setdefault(out_part_id, set()).add(out_name)
            var_name_to_result[out_name] = rewritten_output

        # Mapping of nodes to their successors; used to compute the topological order
        pid_to_needing_pids: Dict[PartId, Set[PartId]] = {
                pid: set() for pid in self.seen_part_ids}
        pid_to_needed_pids: Dict[PartId, Set[PartId]] = {
                pid: set() for pid in self.seen_part_ids}

        for (pid_target, pid_dependency), var_names in \
                self.part_pair_to_edges.items():
            pid_to_needing_pids[pid_dependency].add(pid_target)
            pid_to_needed_pids[pid_target].add(pid_dependency)

            for var_name in var_names:
                pid_to_output_names[pid_dependency].add(var_name)
                pid_to_input_names[pid_target].add(var_name)

        from pytools.graph import compute_topological_order, CycleError
        try:
            toposorted_part_ids = compute_topological_order(pid_to_needing_pids)
        except CycleError:
            raise PartitionInducedCycleError

        return GraphPartition(
                    parts={
                        pid: GraphPart(
                            pid=pid,
                            needed_pids=frozenset(pid_to_needed_pids[pid]),
                            user_input_names=frozenset(
                                self.pid_to_user_input_names.get(pid, set())),
                            partition_input_names=frozenset(pid_to_input_names[pid]),
                            output_names=frozenset(pid_to_output_names[pid]),
                            )
                        for pid in self.seen_part_ids},
                    var_name_to_result=var_name_to_result,
                    toposorted_part_ids=toposorted_part_ids)
Beispiel #4
0
def _toposort_of_subset_of_insns(kernel, subset_insns):
    """
    Returns a :class:`list` of insn ids which is a topological sort of insn
    deps in *subset_insns*.

    :arg subset_insns: a :class:`frozenset` of insn ids that are a subset of
        kernel over which we wish to compute the topological sort.
    """
    dag = {
        insn_id: set(kernel.id_to_insn[insn_id].depends_on
                     & subset_insns)
        for insn_id in subset_insns
    }

    from pytools.graph import compute_topological_order

    return compute_topological_order(dag)[::-1]
Beispiel #5
0
def test_prioritzed_topological_sort():

    import random
    from pytools.graph import compute_topological_order
    rng = random.Random(0)

    def generate_random_graph(nnodes):
        graph = {i: set() for i in range(nnodes)}
        for i in range(nnodes):
            # to avoid cycles only consider edges node_i->node_j where j > i.
            for j in range(i + 1, nnodes):
                # Edge probability 4/n: Generates decently interesting inputs.
                if rng.randint(0, nnodes - 1) <= 2:
                    graph[i].add(j)
        return graph

    nnodes = rng.randint(40, 100)
    rev_dep_graph = generate_random_graph(nnodes)
    dep_graph = {i: set() for i in range(nnodes)}

    for i in range(nnodes):
        for rev_dep in rev_dep_graph[i]:
            dep_graph[rev_dep].add(i)

    keys = [rng.random() for _ in range(nnodes)]
    topo_order = compute_topological_order(rev_dep_graph, key=keys.__getitem__)

    for scheduled_node in topo_order:
        nodes_with_no_deps = {
            node
            for node, deps in dep_graph.items() if len(deps) == 0
        }

        # check whether the order is a valid topological order
        assert scheduled_node in nodes_with_no_deps
        # check whether priorites are upheld
        assert keys[scheduled_node] == min(keys[node]
                                           for node in nodes_with_no_deps)

        # 'scheduled_node' is scheduled => no longer a dependency
        dep_graph.pop(scheduled_node)

        for node, deps in dep_graph.items():
            deps.discard(scheduled_node)

    assert len(dep_graph) == 0
Beispiel #6
0
def preprocess(outputs: DictOfNamedArrays) -> PreprocessResult:
    """Preprocess a computation for code generation."""
    from pytato.transform import copy_dict_of_named_arrays, get_dependencies

    # {{{ compute the order in which the outputs must be computed

    # semantically order does not matter, but doing a toposort ordering of the
    # outputs leads to a FLOP optimal choice

    from pytools.graph import compute_topological_order

    deps = get_dependencies(outputs)

    # only look for dependencies between the outputs
    deps = {
        name: (val & frozenset(outputs.values()))
        for name, val in deps.items()
    }

    # represent deps in terms of output names
    output_to_name = {output: name for name, output in outputs.items()}
    dag = {
        name: (frozenset([output_to_name[output]
                          for output in val]) - frozenset([name]))
        for name, val in deps.items()
    }

    output_order: List[str] = compute_topological_order(dag)[::-1]

    # }}}

    mapper = CodeGenPreprocessor(Namespace())

    new_outputs = copy_dict_of_named_arrays(outputs, mapper)

    return PreprocessResult(outputs=new_outputs,
                            compute_order=tuple(output_order),
                            bound_arguments=mapper.bound_arguments)