Skip to content
cpbenchy 0.1.0.dev0 is in alpha: until version 1.0, commands, options, the Python API and the result format may still change. Pin the version you use.

formulations.py

Compares different ways of modelling the same problem, without any instance files.

Updated View as Markdown
Examplecopy it from the repository's examples/
python examples/scripts/formulations.py --sizes 8 32 64
Distributed as
a file in examples/, to copy
Last updated
6 Oct 2026 · 1 commit
Authors
ThomSerg
Shows
generated instances, and a Loader that builds the model in the worker
Tags
loadermodelling

What it does

Compares formulations of your own model on generated instances: no instance files at all. The instances are sizes, and a loader with an argument builds the model inside the run: N-Queens with AllDifferent constraints, or with a != between every pair of queens. Each formulation is a separate entry of one experiment, so the results stay apart.

Use it

python examples/scripts/formulations.py --sizes 8 32 64 --solvers ortools exact --time-limit 30

Options

Argument
--sizes the board sizes
--solvers the solvers to compare
--time-limit seconds per run
--out output directory

Implementation

The file examples/scripts/formulations.py, 53 lines, as of this version of the docs.

examples/scripts/formulations.pypython
"""Compare formulations of your own model, on generated instances: no instance files at all.

    python examples/scripts/formulations.py --sizes 8 32 64 --solvers ortools exact --time-limit 30

The instances are just sizes (in their `metadata`), and a `Loader` with an argument builds the model in
the worker: N-Queens with AllDifferent constraints, or with a != between every pair of queens. Each
formulation is one `add()` with its own loader; results record the loader, so they stay apart.
"""

import argparse

import cpbenchy
from cpbenchy import Instance


class NQueens(cpbenchy.Loader):
    def __init__(self, formulation):
        self.formulation = formulation  # "alldifferent" or "pairwise"

    def load(self, instance):
        import cpmpy as cp

        n = instance.metadata["n"]
        queens = cp.intvar(0, n - 1, shape=n, name="queen")  # the column of the queen in each row
        diagonals = [queens[i] + i for i in range(n)], [queens[i] - i for i in range(n)]
        if self.formulation == "alldifferent":
            return cp.Model(cp.AllDifferent(queens), *(cp.AllDifferent(d) for d in diagonals))
        return cp.Model(
            [queens[i] != queens[j] for i in range(n) for j in range(i + 1, n)],
            [d[i] != d[j] for d in diagonals for i in range(n) for j in range(i + 1, n)],
        )


def main(argv=None):
    parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument("--sizes", type=int, nargs="+", default=[8, 32, 64])
    parser.add_argument("--solvers", nargs="+", default=["ortools"])
    parser.add_argument("--time-limit", type=float, default=30)
    parser.add_argument("--out", default="results/nqueens")
    args = parser.parse_args(argv)

    instances = [Instance(path=f"nqueens/{n}", name=f"nqueens-{n}", metadata={"n": n}) for n in args.sizes]
    exp = cpbenchy.Experiment(args.out, time_limit=args.time_limit, quiet=True)
    for formulation in ("alldifferent", "pairwise"):
        exp.add(instances, solvers=args.solvers, loader=NQueens(formulation))

    for r in sorted(exp.run(), key=lambda r: (r.instance, r.solver, r.loader)):
        times = f"build {r.parse_s:6.2f}s  solve {r.solve_s:6.2f}s"
        print(f"{r.instance:<12} {r.solver:<8} {r.loader:<26} {r.status:<9} {times}")


if __name__ == "__main__":
    main()
Navigation

Type to search…

↑↓ navigate↵ selectEsc close