A twenty-minute path from pip install to a solved instance, entirely
on the CPU. The GPU is what makes coldcore fast; it is not what makes
it work, and nothing on this page needs one.
git clone https://github.com/Mapika/coldcore
cd coldcore
pip install -e . # or: pip install -e ".[test]" for the suiteRequirements: Python 3.10+ and numpy. That is the whole runtime dependency list, on purpose — the GPU bindings are ctypes, so there is no pybind11/nanobind/torch in the tree.
The editable install needs a pip that speaks PEP 660. Ubuntu 22.04 ships pip 22.0.2, which refuses it with "build backend is missing the 'build_editable' hook";
python -m pip install --upgrade pipfirst and it works. A plainpip install .needs nothing special.
The distribution is pure Python (py3-none-any), so it installs the
same way on x86-64 and on aarch64 (Grace/GH200) with no compiler
involved. What you get from pip:
| you get | you do not get |
|---|---|
the search core (coldcore.search) |
the CUDA plugins |
the CLI (coldcore …) |
libcoldcore.so |
| the numpy reference backends | the big-memory placement policy |
| the backend protocol to write your own |
Check the install from outside the checkout, so you know you are testing the installed package and not the source directory:
cd /tmp
coldcore info
python -m coldcore --helpcoldcore info prints the version and probes for a plugin .so;
reporting plugin: not loadable is the expected answer on a pip-only
install.
K_2(4,1) — the smallest interesting covering code: binary words of
length 4, every word within Hamming distance 1 of a codeword. The
answer is 4.
coldcore solve --backend ref -p q=2 -p n=4 -p R=1 --floor 4[coldcore] feasible at M=4; descending (floor=4)
[coldcore] SOLVED M=4
[coldcore] descent ended at M=4 stats={...}
solve runs greedy build → peel → notch descent. Useful flags:
| flag | meaning |
|---|---|
--backend ref | cpu | gpu |
brute-force numpy oracle, the fast numpy axis-DP backend, or a CUDA plugin |
-p key=value |
problem parameters, repeatable (see below) |
--floor N |
stop descending at size N |
--hours H |
wall-clock budget for the whole run |
--out FILE |
write every improvement atomically (crash-safe) |
--seed-file FILE |
start from an existing solution instead of greedy |
--notch-budget S |
seconds of LNS per notch attempt |
Problem parameters are -p pairs:
| parameter | meaning |
|---|---|
problem= |
hamming (default), torus_linf, grid_linf, lee |
q=, n= |
homogeneous alphabet: the cube q^n |
axes=9x9 |
mixed-radix axes, instead of q/n |
R= |
coverage radius |
mu= |
µ-fold covering (every point covered ≥ µ times; default 1) |
So a 9×9 king-torus dominating set is:
coldcore solve --backend ref -p problem=torus_linf -p axes=9x9 -p R=1Reference backends are brute force and capped at 2^16 points — they are ground truth for parity tests and for learning the API, not a production path.
Three self-contained scripts, each of which asserts its own expected
result, so they double as tests (tests/test_examples.py runs them):
python examples/01_covering_code.py # K_3(4,1) = 9, K_3(4,2) = 3
python examples/02_domination.py # 9x9 king torus = 9 kings
python examples/03_custom_problem.py # a brand-new problem, ~90 linesRead them in that order. The third is the one that matters if you came
here to extend coldcore: it adds toroidal queen domination — a problem
whose coverage neighborhood is not a metric ball at all — as a single
class implementing coldcore.protocol.Backend, and the stock search
core solves it to proven optimality with no changes anywhere else.
The three-line version:
import numpy as np
from coldcore.reference import RefCoveringBackend
from coldcore.search import Searcher
backend = RefCoveringBackend(3, 4, 1) # K_3(4,1), optimum 9
solutions = []
s = Searcher(backend, seed=1,
on_solved=lambda M, idx: solutions.append((M, idx.copy())))
s.load(np.zeros(0, dtype=np.int64)) # start empty
s.greedy_fill(target_m=10 ** 9) # exact lazy greedy
s.descend(floor=9, notch_budget_s=10) # notch ladder + LNS repair
M, idx = min(solutions, key=lambda t: t[0]) # -> 9
print(backend.index_word(idx)) # (9, 4) digit rowsTwo habits worth picking up early:
- Read results from
on_solved, not fromsearcher.code. The callback fires once per verified feasible size.searcher.codeis working state and can be mid-ruin when a budget expires. - Verify outside the framework. House rule from
CONTRIBUTING.md: the search core reports solutions, an independent check decides what is real. All three examples re-derive coverage from the digit rows without touching the backend that produced them.
docs/api.md is the tour of the rest of the surface.
The CUDA plugins are built by cmake, not by pip:
cmake -B build -DCMAKE_BUILD_TYPE=Release # needs CUDA toolkit >= 12
cmake --build build -jThis produces build/lib/libcoldcore.so and
build/plugins/libcoldcore_covering.so. Then --backend gpu works:
coldcore solve --backend gpu -p q=2 -p n=4 -p R=1coldcore.gpu.default_plugin_path() looks for the plugin at
<repo>/build/plugins/libcoldcore_covering.so, relative to the repo
root (three levels up from src/coldcore/gpu.py). That resolves
correctly for a source checkout and for
pip install -e .; for a non-editable install, set
COLDCORE_PLUGIN_PATH to the absolute path of the .so (or pass
--plugin /path/to/libcoldcore_covering.so).
Other environment variables:
| variable | meaning |
|---|---|
COLDCORE_PLUGIN_PATH |
absolute path of the plugin .so to load |
COLDCORE_HBM_BUDGET |
bytes of device memory the placement policy may use (default 80e9) |
COLDCORE_CUDA_ARCHS |
cmake: CUDA architectures to compile for (default 90) |
COLDCORE_FORCE_GPU_TESTS |
run @pytest.mark.gpu tests without a /dev/nvidia* node |
Hardware notes and the memory-placement study are in benchmarks.md and performance.md; the C ABI the plugins speak is in PLUGIN.md and the walkthrough for writing one is plugin-authoring.md.
python -m pytest tests -k "not gpu" # CPU suite, seconds
python -m pytest tests # adds GPU parity if a device is presentGPU tests skip themselves when there is no CUDA device or no built plugin, so the second command is safe to run anywhere.