理解和克服 snakemake 中的 AmbiguousRuleException

Understanding and overcoming AmbiguousRuleException in snakemake

我有一个复杂的工作流程,我逐渐扩展了它。最后一个扩展导致 AmbiguousRuleException。我试图在以下示例中重现工作流的关键结构:

NUMBERS = ["1", "2"]
LETTERS = ["a", "b", "c"]
WORDS = ["foo", "bar", "baz"]
CHOICES = ["yes", "no"]


rule all:
    input:
        # (1)
        expand("results/allthings/{word}_{choice}.md5sum", word=WORDS, choice=CHOICES)
        #expand("results/allthings/{word}_{choice}.md5sum", word=WORDS + ["all"], choice=CHOICES)

rule make_things:
    output:
        "results/{letter}_{number}/{word}_{choice}.txt"
    shell:
        """
        echo "{wildcards.letter}_{wildcards.number}_{wildcards.word}_{wildcards.choice}" > {output}
        """

rule gather_things:
    input:
        expand("results/{letter}_{number}/{{word}}_{{choice}}.txt", letter=LETTERS, number=NUMBERS)
    output:
        "results/allthings/{word}_{choice}.txt"
    shell:
        """
        cat {input} > {output}
        """

# (2)
#rule join_all_words:
#    input:
#        expand("results/allthings/{word}_{{choice}}.txt", word=WORDS)
#    output:
#        "results/allthings/all_{choice}.txt"
#    shell:
#        """
#        cat {input} > {output}
#        """
# (3)
#def source_data(wildcards):
#    if wildcards.word == "all":
#        return rules.join_all_words.output
#    else:
#        return rules.gather_things.output

rule compute_md5:
    input:
        # (4)
        rules.gather_things.output,
        #source_data
    output:
        "results/allthings/{word}_{choice}.md5sum"
    shell:
        """
        md5sum {input} > {output}
        """

以上状态有效。切换 (1)(4) 并取消注释 (2)(3) 对应于我正在尝试进行的扩展,并导致以下失败:

AmbiguousRuleException:
Rules gather_things and join_all_words are ambiguous for the file results/allthings/all_yes.txt.
Expected input files:
    gather_things: results/a_1/all_yes.txt results/a_2/all_yes.txt results/b_1/all_yes.txt results/b_2/all_yes.txt results/c_1/all_yes.txt results/c_2/all_yes.txt
    join_all_words: results/allthings/foo_yes.txt results/allthings/bar_yes.txt results/allthings/baz_yes.txt

好像snakemake认为results/allthings/all_yes.txt可以通过gather_things生成。

为什么?

我怎样才能避免这种情况?


注意:修改 (3)(4) 的目的是让 compute_md5gather_things 的直接输出上工作(对于 foo , barbaz) 以及三者的联合输出 (all), 尽可能保持根据其他规则的输出定义输入 (这使得更改比文件名时更容易明确使用)。

2017-07-28 Post 为简洁起见进行了编辑

起初我以为只是歧义。前三点与解决歧义有关。之后,我解释了如何概括 'compute_md5' 以实现所需的行为。

控制歧义

1) 控制流歧义:

规则顺序 http://snakemake.readthedocs.io/en/latest/snakefiles/rules.html?highlight=ruleorder#handling-ambiguous-rules

以下情况建议避免。在模块化的宏伟希望中,通过使用 "ruleorder" 本质上是将两个规则耦合在一起。 "ruleorder" 功能只有在 Snakefile 的范围内存在两个规则时才能使用。如果规则并不总是一起提供,这可能是模块化的问题。如果它们的规则总是一起提供,我认为它们已经耦合,这样做不会使情况变得更糟,事实上,会增加凝聚力。当使用 'constraints' 不够时使用 "ruleorder",因为有时 where 将不可避免地产生歧义。

https://en.wikipedia.org/wiki/GRASP_(object-oriented_design)

有条件的'includes' https://github.com/tboyarski/BCCRC-Snakemake/tree/master/modules/bamGen

规则顺序在“_INCLUDE”中 sam2BAM 和 bamALIGN_bwa 的输出非常相似,主要是因为 sam2BAM 非常通用。

因为 bamALIGN_bwa 和 bamALIGN_star 在技术上是可以切换的,我不希望用户为了在它们之间切换而交换规则顺序,我有一个布尔值存储在我的 YAML 文件中,以充当硬过滤器,从字面上阻止 Snakemake 甚至看到规则。这在您只能选择一个或另一个的情况下非常有效(在这种情况下,两个对齐器有自己的参考基因组。我强迫用户在我的管道开始时设置参考基因组,这样用户实际上永远不会 运行 两者。我还没有实现检测正在使用哪个参考基因组的功能,以便随后选择相应的对齐器。这将是一些开销 python 代码,好主意,但目前尚未实现)。

2) 让Snakemake忽略歧义

有超车。它存在,但我认为应尽可能避免“--allow-ambiguity”。

http://snakemake.readthedocs.io/en/latest/snakefiles/rules.html?highlight=--allow-ambiguity#handling-ambiguous-rules

3) 优雅~防止歧义

http://snakemake.readthedocs.io/en/latest/snakefiles/rules.html?highlight=wildcard_constraints#wildcards

rule gather_things:
     input:
         expand("results/{letter}_{number}/{{word}}_{{choice}}.txt", letter=LETTERS, number=NUMBERS)
     output:
         "results/allthings/{word}_{choice}.txt"
      wildcard_constraints:
         word='[^(all)][0-9a-zA-Z]*'
...

此规则需要 wildcard_constraint,以防止它与 "join_all_words" 规则竞争。这很容易通过防止此处的通配符 "word" 成为字符串 'all' 来完成。这使得 "gather_things" 和 "join_all_words" 可微分。

compute_md5 泛化能力

至于让"compute_md5"同时接受"gather_things"和"join_all_words"的输入,这需要使其更通用,与歧义无关。接下来您需要做的是调整 "join_all_words" 规则,使其不依赖于任何给定规则的输入。

https://github.com/tboyarski/BCCRC-Snakemake/blob/master/help/download.svg

我只想感谢您提供了一个 TOP-NOTCH 示例。太棒了!

 NUMBERS = ["1", "2"]
 LETTERS = ["a", "b", "c"]
 WORDS = ["foo", "bar", "baz"]
 CHOICES = ["yes", "no"]


 rule all:
     input:
         expand("results/allthings/all_{choice}.md5sum", choice=CHOICES),
         expand("results/allthings/{word}_{choice}.md5sum", word=WORDS, choice=CHOICES)

 rule make_things:
     output:
         "results/{letter}_{number}/{word}_{choice}.txt"
     shell:
         """
         echo "{wildcards.letter}_{wildcards.number}_{wildcards.word}_{wildcards.choice}" > {output}
         """

 rule gather_things:
     input:
         expand("results/{letter}_{number}/{{word}}_{{choice}}.txt", letter=LETTERS, number=NUMBERS)
     output:
         "results/allthings/{word}_{choice}.txt"
     wildcard_constraints:
         word='[^(all)][0-9a-zA-Z]*'
     shell:
         """
         cat {input} > {output}
         """

 rule join_all_words:
     input:
         expand("results/allthings/{word}_{{choice}}.txt", word=WORDS)
     output:
         "results/allthings/all_{choice}.txt"
     shell:
         """
         cat {input} > {output}
         """

 rule compute_md5:
     input:
         "{pathCMD5}/{sample}.txt"
     output:
         "{pathCMD5}/{sample}.md5sum"
         #"results/allthings/{word}_{choice}.md5sum"
     shell:
         """
         md5sum {input} > {output}