理解和克服 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_md5
在 gather_things
的直接输出上工作(对于 foo
, bar
和 baz
) 以及三者的联合输出 (all
), 尽可能保持根据其他规则的输出定义输入 (这使得更改比文件名时更容易明确使用)。
2017-07-28 Post 为简洁起见进行了编辑
起初我以为只是歧义。前三点与解决歧义有关。之后,我解释了如何概括 'compute_md5' 以实现所需的行为。
控制歧义
1) 控制流歧义:
以下情况建议避免。在模块化的宏伟希望中,通过使用 "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”。
3) 优雅~防止歧义
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}
我有一个复杂的工作流程,我逐渐扩展了它。最后一个扩展导致 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_md5
在 gather_things
的直接输出上工作(对于 foo
, bar
和 baz
) 以及三者的联合输出 (all
), 尽可能保持根据其他规则的输出定义输入 (这使得更改比文件名时更容易明确使用)。
2017-07-28 Post 为简洁起见进行了编辑
起初我以为只是歧义。前三点与解决歧义有关。之后,我解释了如何概括 'compute_md5' 以实现所需的行为。
控制歧义
1) 控制流歧义:
以下情况建议避免。在模块化的宏伟希望中,通过使用 "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”。
3) 优雅~防止歧义
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}