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.

Recipes

Short observers and plugins for common needs, each with the simplest tool for the job.

Updated View as Markdown

Each recipe is a complete file: save it, and use it with -p file.py, or plugins=["file.py"] in Python.

Record something about each model

import cpbenchy

class Variables(cpbenchy.Observer):
    def on_finish(self, ctx):
        if ctx.model is None:  # loading failed
            return
        from cpmpy.transformations.get_variables import get_variables_model

        ctx.record("n_variables", len(get_variables_model(ctx.model)))

ModelSize in the library records the number of variables and constraints.

import cpbenchy

class Live(cpbenchy.Observer):
    def on_event(self, run, event):
        if event.kind == "solution":
            print(f"{run.spec.instance.name} {run.spec.solver}: {event.data['objective']} after {event.t:.1f}s")

This runs in the parent, so it costs the runs nothing. Solutions come for optimization problems, from solvers that report them while solving.

Send each result somewhere

import json

import cpbenchy

class JsonLines(cpbenchy.Observer):
    def __init__(self, path="all-results.jsonl"):
        self.path = path

    def on_result(self, run, result):
        with open(self.path, "a") as f:
            f.write(json.dumps(result.to_dict()) + "\n")

Results are stored in the output directory anyway. This is for collecting them somewhere else as well, such as a shared file or a database: see SqliteStore. To run cpbenchy from your own experiment framework instead, see from your framework.

Settings for a solver

import cpbenchy

class Settings(cpbenchy.Observer):
    def solver_args(self, ctx):
        if ctx.spec.solver == "ortools" and not ctx.model.has_objective():
            return {"num_violation_ls": 1}

These go to solve(), next to what cpbenchy sets itself: the time limit, the seed and the number of cores. Its table of solver parameters for the seed and the cores is cpbenchy.worker.solvers.NATIVE. For settings per run, give them as the run’s params instead: they are part of what the run measures.

Your own instances

Write a loader.

Only some of the runs

Choosing runs is a hook, cpbenchy_modify_runs. It gets all the runs, and changes the list in place:

import cpbenchy

@cpbenchy.hookimpl
def cpbenchy_modify_runs(config, runs):
    runs[:] = [r for r in runs if not (r.solver == "exact" and r.instance.name.startswith("huge"))]

An option of your own

See the first plugin: an option, and state across runs.

Run the workers somewhere else

Return your own cpbenchy.executors.Executor from the hook cpbenchy_make_executor, and implement execute(job) -> Measurement. The job has the command, limits, CPUs, log file and environment. This is how containers and remote machines plug in.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close