A competition runs your solver itself. Its own tool (such as runsolver) starts the solver on each
instance with a command line you register, limits and measures it, and reads its standard output.
cpbenchy submission builds what you send in, from one small file:
cpbenchy submission init --rules xcsp3-2025 --rules xcsp3-2025-fast -s ortools
# edit submission.toml: tracks, test instances, the solver description
cpbenchy submission build --test --archive zipbuilt cpmpy-ortools
COP DIR/bin/run_COP BENCHNAME RANDOMSEED TIMELIMIT MEMLIMIT NBCORE TMPDIR
FAST-COP DIR/bin/run_FAST-COP BENCHNAME RANDOMSEED TIMELIMIT MEMLIMIT NBCORE TMPDIR
ok COP instances/small.xml
ok FAST-COP instances/small.xml
archive cpmpy-ortools.zipRegister those command lines with the competition, upload the archive, and that’s it.
The submission file
name = "cpmpy-{solver}" # {solver} is filled in, so one file serves several solvers
description = "CPMpy, with {solver} as its backend"
authors = ["..."]
solver = "ortools"
params = {} # solver parameters, as -P
plugins = [] # extra plugins: module references, or .py files (bundled)
loader = "myloaders.py:MyLoader" # optional, as --loader (bundled)
requirements = ["pycsp3"] # extra packages, e.g. pycsp3 to read XCSP3
include = ["doc/description.pdf"] # files or directories to copy in
[[tracks]]
name = "COP"
rules = "xcsp3-2025" # built-in rules or a .toml file
test = ["instances/small.xml"] # instances for `build --test`
[[tracks]]
name = "FAST-COP"
rules = "xcsp3-2025-fast"
test = ["instances/small.xml"]Each track follows rules: limits, how the run is stopped, which output it prints. The
rules’ [interface] says how the competition calls a solver: which placeholders the launcher takes, in
which order. A track can override solver, params and arguments.
cpbenchy submission build -s highs builds the same file for another solver, as cpmpy-highs.
What it builds
cpmpy-ortools/
bin/run_COP a launcher per track
install.sh run once where it will run: a .venv with requirements.txt
requirements.txt the packages, pinned to the versions it was built and tested with
code/ cpbenchy itself, CPMpy if installed from a local source, and bundled .py files
rules/ the rules of each track
README.md what to register: each track's command line
build.json how it was built: versions, the submission file, the rules
doc/... what you includedEach launcher passes the placeholders on to python -m cpbenchy solve and execs it, so the
competition’s signals reach the solver process directly. It also sets OPENBLAS_NUM_THREADS=1:
otherwise numpy starts a thread per CPU when CPMpy is imported, which costs CPU time that competitions
count.
| Placeholder | Passed as |
|---|---|
BENCHNAME |
the instance |
RANDOMSEED |
--seed (solvers get it modulo 2³¹) |
TIMELIMIT, TIMEOUT |
--cpu-time-limit if the rules limit CPU time, else --time-limit |
WALLTIMELIMIT |
--time-limit |
MEMLIMIT |
--mem-limit |
NBCORE, NBCORES |
--cores |
TMPDIR |
the TMPDIR environment variable |
A competition with other placeholders, or a fixed calling convention, is described in the rules’
[interface]. Map a placeholder yourself as "NAME=--option", or ignore one as "NAME=". For example,
for a MaxSAT Evaluation anytime track, called as run <instance> <seconds>:
[interface]
arguments = ["BENCHNAME", "TIMELIMIT=--time-limit"]Packages
requirements.txt pins everything the submission needs, dependencies included, to the versions
installed where you build it. That covers what cpbenchy solve needs, CPMpy, the solvers’ packages,
and your requirements. Build it in the environment you tested with. cpbenchy itself is in code/,
so it needs no install, and neither does BenchExec: the competition measures the runs.
install.sh creates .venv with python3 -m venv (or virtualenv), or with the Python in PYTHON,
and installs requirements.txt. For machines without internet, build --wheels downloads the wheels
into wheels/, and install.sh then installs from there. Build on the same platform and Python
version as the competition’s machines, as wheels are specific to both.
Testing
build --test runs each track’s launcher on its test instances, as the competition would: with its
placeholders, from another directory, using this Python and the submission’s own code/. It checks
that each run prints one s line. --test-time sets the time limit (default 20 seconds).
To test the installed submission itself, run a launcher by hand:
./install.sh
bin/run_COP instances/small.xml 1 60 4096 1 /tmpcpbenchy solve
The launchers run cpbenchy solve, which solves one instance in its own process, as a competition
solver does. It takes the options of cpbenchy run, for one instance, solver and seed:
cpbenchy solve instance.xml -s ortools --rules xcsp3-2025 --cpu-time-limit 60c [cpbenchy 0.81s] loading instance.xml
o 12
o 9
s OPTIMUM FOUND
v <instantiation type="optimum" cost="9">
...
c cpbenchy: optimal, objective 9; parse 0.07s, transform 0.00s, solve 0.38s- Competition output goes to stdout. Progress and a summary come as comment lines (
c ...). - It always stops at its limits, as with
--terminate. At its time limit (wall or CPU), or on SIGTERM, it prints the best solution it found. - Time counts from when the process started, as the competition counts it.