    private List<DAGNode<TaskGraph.Node, TaskGraph.Edge>>
    makeMergedAlgoNodes(ExperimentSuite experiments, MeasurementSuite measurements, TTDataSet dataset, ExperimentOutputs outputs,
                        DAGNode<TaskGraph.Node, TaskGraph.Edge> commonDep) throws RecommenderConfigurationException {
        List<DAGNode<TaskGraph.Node, TaskGraph.Edge>> nodes = Lists.newArrayList();
        MergePool<CachedSatisfaction,DesireChain> mergePool = MergePool.create();
        List<DAGNode<CachedSatisfaction,DesireChain>> graphs = Lists.newArrayList();
        Set<DAGNode<CachedSatisfaction,DesireChain>> allNodes = Sets.newHashSet();
        ComponentCache cache = new ComponentCache(cacheDir);
        for (AlgorithmInstance algo: experiments.getAlgorithms()) {
            logger.debug("building graph for algorithm {}", algo);
            DAGNode<CachedSatisfaction, DesireChain> graph =
                    algo.buildRecommenderGraph(dataset.getTrainingData().getConfiguration());
            graph = mergePool.merge(graph);
            logger.debug("algorithm {} has {} new configuration nodes", algo,
                         Sets.difference(allNodes, graph.getReachableNodes()).size());
            allNodes.addAll(graph.getReachableNodes());
            LenskitEvalJob job = new LenskitEvalJob(algo, dataset, measurements,
                                                    outputs.getPrefixed(algo, dataset),
                                                    graph, cache);
            DAGNodeBuilder<TaskGraph.Node, TaskGraph.Edge> nb = DAGNode.newBuilder();
            nb.setLabel(TaskGraph.jobNode(job));
            if (commonDep != null) {
                nb.addEdge(commonDep, TaskGraph.edge());
            }
            assert nodes.size() == graphs.size();
            Set<DAGNode<CachedSatisfaction,DesireChain>> seen = Sets.newHashSet();
            logger.debug("finding dependencies of {}", job);
            for (int i = 0; i < nodes.size(); i++) {
                DAGNode<CachedSatisfaction, DesireChain> other = graphs.get(i);
                Set<DAGNode<CachedSatisfaction,DesireChain>> freshCommon = Sets.newHashSet();
                freshCommon.addAll(other.getReachableNodes());
                freshCommon.addAll(graph.getReachableNodes());
                freshCommon.removeAll(seen);
                if (!freshCommon.isEmpty()) {
                    logger.debug("{} depends on {} for {} nodes",
                                 job, nodes.get(i).getLabel(),
                                 freshCommon.size());
                    for (DAGNode<CachedSatisfaction,DesireChain> shared: freshCommon) {
                        logger.debug("it will reuse {}",
                                     shared.getLabel().getSatisfaction());
                    }
                    nb.addEdge(nodes.get(i), TaskGraph.edge());
                }
                seen.addAll(freshCommon);
            }
            graphs.add(graph);
            DAGNode<TaskGraph.Node, TaskGraph.Edge> jobNode = nb.build();
            logger.debug("{} has {} dependencies", job, jobNode.getAdjacentNodes().size());
            nodes.add(jobNode);
        }
        return nodes;
    }

    private List<DAGNode<TaskGraph.Node, TaskGraph.Edge>>
    makeMergedAlgoNodes(ExperimentSuite experiments, MeasurementSuite measurements, TTDataSet dataset, ExperimentOutputs outputs,
                        DAGNode<TaskGraph.Node, TaskGraph.Edge> commonDep) throws RecommenderConfigurationException {
        List<DAGNode<TaskGraph.Node, TaskGraph.Edge>> nodes = Lists.newArrayList();
        MergePool<CachedSatisfaction,DesireChain> mergePool = MergePool.create();
        List<DAGNode<CachedSatisfaction,DesireChain>> graphs = Lists.newArrayList();
        Set<DAGNode<CachedSatisfaction,DesireChain>> allNodes = Sets.newHashSet();
        ComponentCache cache = new ComponentCache(cacheDir);
        for (AlgorithmInstance algo: experiments.getAlgorithms()) {
            logger.debug("building graph for algorithm {}", algo);
            DAGNode<CachedSatisfaction, DesireChain> graph =
                    algo.buildRecommenderGraph(dataset.getTrainingData().getConfiguration());
            graph = mergePool.merge(graph);
            logger.debug("algorithm {} has {} new configuration nodes", algo,
                         Sets.difference(graph.getReachableNodes(), allNodes).size());
            allNodes.addAll(graph.getReachableNodes());
            LenskitEvalJob job = new LenskitEvalJob(algo, dataset, measurements,
                                                    outputs.getPrefixed(algo, dataset),
                                                    graph, cache);
            DAGNodeBuilder<TaskGraph.Node, TaskGraph.Edge> nb = DAGNode.newBuilder();
            nb.setLabel(TaskGraph.jobNode(job));
            if (commonDep != null) {
                nb.addEdge(commonDep, TaskGraph.edge());
            }
            assert nodes.size() == graphs.size();
            Set<DAGNode<CachedSatisfaction,DesireChain>> seen = Sets.newHashSet();
            logger.debug("finding dependencies of {}", job);
            for (int i = 0; i < nodes.size(); i++) {
                DAGNode<CachedSatisfaction, DesireChain> other = graphs.get(i);
                Set<DAGNode<CachedSatisfaction,DesireChain>> freshCommon = Sets.newHashSet();
                freshCommon.addAll(graph.getReachableNodes());
                freshCommon.retainAll(other.getReachableNodes());
                freshCommon.removeAll(seen);
                if (!freshCommon.isEmpty()) {
                    logger.debug("{} depends on {} for {} nodes",
                                 job, nodes.get(i).getLabel(),
                                 freshCommon.size());
                    for (DAGNode<CachedSatisfaction,DesireChain> shared: freshCommon) {
                        logger.debug("it will reuse {}",
                                     shared.getLabel().getSatisfaction());
                    }
                    nb.addEdge(nodes.get(i), TaskGraph.edge());
                }
                seen.addAll(freshCommon);
            }
            graphs.add(graph);
            DAGNode<TaskGraph.Node, TaskGraph.Edge> jobNode = nb.build();
            logger.debug("{} has {} dependencies", job, jobNode.getAdjacentNodes().size());
            nodes.add(jobNode);
        }
        return nodes;
    }
