def clone_merge(expr, memo=None, merge_literals=False):
    nodes = dfs(expr)
    if memo is None:
        memo = {}
    # -- args are somewhat slow to construct, so cache them out front
    #    XXX node.arg does not always work (builtins, weird co_flags)
    node_args = [(node.pos_args, node.named_args) for node in nodes]
    del node
    for ii, node_ii in enumerate(nodes):
        if node_ii in memo:
            continue
        new_ii = None
        if node_ii.pure:
            for jj in range(ii):
                node_jj = nodes[jj]
                if node_ii.name != node_jj.name:
                    continue
                if node_ii.name == 'literal':
                    if not merge_literals:
                        continue
                    if node_ii._obj != node_jj._obj:
                        continue
                else:
                    if node_args[ii] != node_args[jj]:
                        continue
                logger.debug('clone_merge %s %i <- %i' % (
                    node_ii.name, jj, ii))
                new_ii = node_jj
                break
        if new_ii is None:
            new_inputs = [memo[arg] for arg in node_ii.inputs()]
            new_ii = node_ii.clone_from_inputs(new_inputs)
        memo[node_ii] = new_ii

    return memo[expr]


##############################################################################
##############################################################################
dataset/ETHPy150Open hyperopt/hyperopt/hyperopt/pyll/base.py/clone_merge/original