Search code examples
snakemakeconways-game-of-life

Input Functions in Snakemake


I am building an example Workflow in snakemake, the Game of Life. In this particular example, I am using a starting grid that leads to an empty world after a few iterations. I would like to build a conditional workflow that checks for emptiness after each time step and stops the calculation of new time steps once the world is empty. It should then proceed to render a video from the displays of previous time steps.

To check for emptiness, I am using an input function and a checkpoint. Here is the Snakefile:

configfile: "config_exercise_2.yaml"
workdir: "../"

import numpy as np

Time = 1
def gridEmpty():
    global Time
    with open(checkpoints.CalculateNextTimeStep.get(time=Time).output[0],"rb") as f:
        if np.all(np.load(f) == 0): #grid is empty, no more timesteps required
            return [f"pictures/picture_t-{i}.jpg" for i in range(0,Time+1,1)]
        else: #grid is not empty, more timesteps required
            Time += 1
            checkpoints.CalculateNextTimeStep.get(time=Time)
                                                  
rule RenderVideo:
    input:
        gridEmpty
    output:
        "video/clip.mp4"
    params:
        framerate = config["FrameRate"]
    shell:
        "cat {input} | ffmpeg -framerate {params.framerate} -f image2pipe -i - {output}"
        
rule Displaytimestep:
    input:
        "arrays/array_t-{time}.npy"
    output:
        "pictures/picture_t-{time}.jpg"
    conda:
        "../environment/environment.yaml"
    params:
        timeStep = lambda wildcards: wildcards.time
    script:
        "../scripts/DisplayTimeStep.py"
    
checkpoint CalculateNextTimeStep:
    input:
        lambda wildcards: f"arrays/array_t-{int(wildcards.time)-1}.npy"
    output:
        "arrays/array_t-{time}.npy"
    params:
        GridSize = config["GridSize"]
    conda:
        "../environment/environment.yaml"
    script:
        "../scripts/CalculateNextTimeStep.py"

I remember that this code worked previously, however now I am getting

Error: TypeError: gridEmpty() takes 0 positional arguments but 1 was given Wildcards:

Traceback:

Which puzzles me, since I do not see where any arguments are given to the input function. Any ideas? Thank you!


Solution

  • When you use a function as input, like:

    rule RenderVideo:
        input:
            gridEmpty
    

    snakemake automatically calls the function using the wildcards object as argument to the function. That is, it calls gridEmpty(wildcards). Since your definition of gridEmpty does not take any parameter, you get the error you see.

    The easiest solution may be to define gridEmpty so that it takes one argument even if that argument is not used. Alternatively, this should also work and it is equivalent:

    rule RenderVideo:
        input:
            lambda wildcards: gridEmpty(),
    

    (NB: I haven't checked whether the rest of your code is correct)