I'm trying to write a snakemake workflow that where the wildcards are 'swapped' using an input function. Essentially, there are two conditions ('A' and 'B'), and a number of files are generated for each (A/1.txt, A/2.txt, etc, B/1.txt, B/2.txt, etc) The number of files is always the same between the conditions, but unknown at the start of the workflow. There is an intermediate step, and then I want to use the intermediate files from one condition with the original files from the other condition. I wrote a simple snakefile that illustrates what I want to do:
dirs = ("A", "B")
wildcard_constraints:
DIR='|'.join(dirs),
sample='\d+'
rule all:
input:
expand("{DIR}_done", DIR=dirs)
checkpoint create_files:
output:
directory("{DIR}/")
shell:
"""
mkdir {output};
N=10;
for D in $(seq $N); do
touch {output}/$D.txt
done
"""
rule intermediate:
input:
"{DIR}/{SAMPLE}.txt"
output:
intermediate = "{DIR}_intermediate/{SAMPLE}.txt"
shell:
"touch {intermediate}"
def swap_input(wildcards):
if wildcards.DIR == 'A':
return f'B_intermediate/{wildcards.SAMPLE}.txt'
if wildcards.DIR == 'B':
return f'A_intermediate/{wildcards.SAMPLE}.txt'
rule swap:
input:
original = "{DIR}/{SAMPLE}.txt",
intermediate = swap_input
output:
swapped="{DIR}_swp/{SAMPLE}.txt"
shell:
"touch {output}"
def get_samples(wildcards):
checkpoint_dir = checkpoints.create_files.get(**wildcards).output[0]
samples = glob_wildcards(os.path.join(checkpoint_dir, "{sample}.txt")).sample
return samples
rule done:
input:
lambda wildcards: expand("{DIR}_swp/{SAMPLE}.txt", SAMPLE=get_samples(wildcards), allow_missing=True)
output:
"{DIR}_done"
shell:
"touch {output}"
However, snakemake doesn't appear to be correctly inferring the wildcards. Perhaps I am misunderstanding something about the way snakemake infers wildcards. I get the error:
MissingInputException in rule intermediate in line 38 of Snakefile:
Missing input files for rule intermediate:
output: A_intermediate/1.txt
wildcards: DIR=A, SAMPLE=1
affected files:
A/1.txt
But the file A/1.txt should be created by the first rule create_files.
I thought perhaps this might be something to do with the checkpoint not being completed, but if I add checkpoint_dir = checkpoints.create_files.get(**wildcards).output[0] at the start of the input function swap_input, the error is still the same.
Is there a way to get this workflow to work?