configfile: "config.yaml"


benchmark = config["benchmark"]
SOLVER = config["solver"]
NRANGE = config[benchmark]["nrange"]
APIS = config[benchmark]["apis"]

rule all:
    input:
        expand(
            "benchmarks/{model}/benchmark-{kind}.pdf",
            model=benchmark,
            kind=["absolute", "overhead"],
        ),
        expand(
            "benchmarks/{model}/benchmark_resource-{kind}.pdf",
            model=benchmark,
            kind=["absolute", "overhead"],
        ),


# LINOPY
rule benchmark_time_linopy:
    params:
        nrange=NRANGE,
    output:
        "benchmarks/{model}/linopy/time.csv",
    script:
        "scripts/benchmark_linopy.py"


rule benchmark_memory_single_linopy:
    params:
        nrange=NRANGE,
    benchmark:
        "benchmarks/{model}/linopy/memory-{N}.txt"
    script:
        "scripts/run-linopy.py"


rule benchmark_linopy:
    params:
        nrange=NRANGE,
        api="linopy",
    input:
        memory=lambda w: expand(
            "benchmarks/{model}/linopy/memory-{N}.txt", N=NRANGE, model=benchmark
        ),
        time="benchmarks/{model}/linopy/time.csv",
    output:
        benchmark="benchmarks/{model}/linopy.csv",
    script:
        "scripts/concat-benchmarks.py"


# PULP
rule benchmark_time_pulp:
    params:
        nrange=NRANGE,
    output:
        "benchmarks/{model}/pulp/time.csv",
    script:
        "scripts/benchmark_pulp.py"


rule benchmark_memory_single_pulp:
    params:
        nrange=NRANGE,
    benchmark:
        "benchmarks/{model}/pulp/memory-{N}.txt"
    script:
        "scripts/run-pulp.py"


rule benchmark_pulp:
    params:
        nrange=NRANGE,
        api="pulp",
    input:
        memory=lambda w: expand(
            "benchmarks/{model}/pulp/memory-{N}.txt", N=NRANGE, model=benchmark
        ),
        time="benchmarks/{model}/pulp/time.csv",
    output:
        benchmark="benchmarks/{model}/pulp.csv",
    script:
        "scripts/concat-benchmarks.py"


# CVXPY
rule benchmark_time_cvxpy:
    params:
        nrange=NRANGE,
    output:
        "benchmarks/{model}/cvxpy/time.csv",
    script:
        "scripts/benchmark_cvxpy.py"


rule benchmark_memory_single_cvxpy:
    params:
        nrange=NRANGE,
    benchmark:
        "benchmarks/{model}/cvxpy/memory-{N}.txt"
    script:
        "scripts/run-cvxpy.py"


rule benchmark_cvxpy:
    params:
        nrange=NRANGE,
        api="cvxpy",
    input:
        memory=lambda w: expand(
            "benchmarks/{model}/cvxpy/memory-{N}.txt", N=NRANGE, model=benchmark
        ),
        time="benchmarks/{model}/cvxpy/time.csv",
    output:
        benchmark="benchmarks/{model}/cvxpy.csv",
    script:
        "scripts/concat-benchmarks.py"


# PYOMO
rule benchmark_time_pyomo:
    params:
        nrange=NRANGE,
    output:
        "benchmarks/{model}/pyomo/time.csv",
    script:
        "scripts/benchmark_pyomo.py"


rule benchmark_memory_single_pyomo:
    params:
        nrange=NRANGE,
    benchmark:
        "benchmarks/{model}/pyomo/memory-{N}.txt"
    script:
        "scripts/run-pyomo.py"


rule benchmark_pyomo:
    params:
        nrange=NRANGE,
        api="pyomo",
    input:
        memory=lambda w: expand(
            "benchmarks/{model}/pyomo/memory-{N}.txt", N=NRANGE, model=benchmark
        ),
        time="benchmarks/{model}/pyomo/time.csv",
    output:
        benchmark="benchmarks/{model}/pyomo.csv",
    script:
        "scripts/concat-benchmarks.py"


# GUROBIPY
rule benchmark_time_gurobipy:
    params:
        nrange=NRANGE,
    output:
        "benchmarks/{model}/gurobipy/time.csv",
    script:
        "scripts/benchmark_gurobipy.py"


rule benchmark_memory_single_gurobipy:
    params:
        nrange=NRANGE,
    benchmark:
        "benchmarks/{model}/gurobipy/memory-{N}.txt"
    script:
        "scripts/run-gurobipy.py"


rule benchmark_gurobipy:
    params:
        nrange=NRANGE,
        api="gurobipy",
    input:
        memory=lambda w: expand(
            "benchmarks/{model}/gurobipy/memory-{N}.txt", N=NRANGE, model=benchmark
        ),
        time="benchmarks/{model}/gurobipy/time.csv",
    output:
        benchmark="benchmarks/{model}/gurobipy.csv",
    script:
        "scripts/concat-benchmarks.py"


# ORTOOLS
rule benchmark_time_ortools:
    params:
        nrange=NRANGE,
    output:
        "benchmarks/{model}/ortools/time.csv",
    script:
        "scripts/benchmark_ortools.py"


rule benchmark_memory_single_ortools:
    params:
        nrange=NRANGE,
    benchmark:
        "benchmarks/{model}/ortools/memory-{N}.txt"
    script:
        "scripts/run-ortools.py"


rule benchmark_ortools:
    params:
        nrange=NRANGE,
        api="ortools",
    input:
        memory=lambda w: expand(
            "benchmarks/{model}/ortools/memory-{N}.txt", N=NRANGE, model=benchmark
        ),
        time="benchmarks/{model}/ortools/time.csv",
    output:
        benchmark="benchmarks/{model}/ortools.csv",
    script:
        "scripts/concat-benchmarks.py"


rule benchmark_jump:
    "For time & memory benchmarks, use one process for all runs (skip import and jit compilation times)"
    params:
        nrange=NRANGE,
    output:
        "benchmarks/{model}/jump.csv",
    script:
        "scripts/benchmark_jump.jl"


# For benchmarking solver processes only, we have to start from the lp files
rule write_lp:
    output:
        expand("benchmarks/{model}/lp_files/{N}.lp", model=benchmark, N=NRANGE),
    script:
        "scripts/write-lp-file.py"


rule benchmark_memory_single_gurobi:
    input:
        lp="benchmarks/{model}/lp_files/{N}.lp",
    benchmark:
        "benchmarks/{model}/solver/memory-{N}.txt"
    shell:
        "gurobi_cl ResultFile=/dev/null {input.lp}"


rule benchmark_time_single_gurobi:
    input:
        lp="benchmarks/{model}/lp_files/{N}.lp",
    output:
        time="benchmarks/{model}/solver/time-{N}.txt",
    run:
        import gurobipy

        m = gurobipy.read(input.lp)
        m.optimize()

        with open(output.time, "w") as f:
            f.write(str(m.RunTime))


rule benchmark_gurobi:
    params:
        nrange=NRANGE,
        api="Solving Process",
    input:
        memory=expand("benchmarks/{model}/solver/memory-{N}.txt", model=benchmark, N=NRANGE),
        time=expand("benchmarks/{model}/solver/time-{N}.txt", model=benchmark, N=NRANGE),
    output:
        benchmark="benchmarks/{model}/solver.csv",
    script:
        "scripts/concat-benchmarks.py"


# Merge all benchmarks
rule merge_benchmarks:
    params:
        nrange=NRANGE,
        apis=[api for api in APIS if api != "solver"],
    input:
        benchmarks=lambda w: expand(
            "benchmarks/{model}/{api}.csv", model=benchmark, api=APIS
        ),
    output:
        absolute="benchmarks/{model}/benchmarks-absolute.csv",
        overhead="benchmarks/{model}/benchmarks-overhead.csv",
    script:
        "scripts/merge-benchmarks.py"


rule plot_benchmarks:
    input:
        "benchmarks/{model}/benchmarks-{kind}.csv",
    output:
        time_memory = "benchmarks/{model}/benchmark-{kind}.pdf",
        resource = "benchmarks/{model}/benchmark_resource-{kind}.pdf",
    notebook:
        "notebooks/plot-benchmarks.py.ipynb"
