Skip to main content

cpsat-logutils

Parse CP-SAT (OR-Tools) solver logs into line-anchored, JSON-serializable pydantic models.

pip install cpsat-logutils
from cpsat_logutils import parse_log

log = parse_log(open("solver.log").read())

log.solver.version.value  # "9.15.6755"
log.solver.parameters.value  # {"max_time_in_seconds": 5, "num_workers": 8, ...}
log.response.status.value  # "OPTIMAL"
log.response.status.line  # 346  <- every value knows its line
log.search.events[0].objective  # first solution value
log.stats.search_stats.row("core").values["Conflicts"]
log.block_at(line=42)  # -> BlockRef(kind="presolve", span=..., path="/presolve")
log.model_dump_json(indent=2)  # plain JSON

Sections that do not occur in a log are simply None. Nothing raises on a log the parser does not fully understand: unknown sections are kept verbatim in log.unparsed.

Getting a log

from ortools.sat.python import cp_model

solver = cp_model.CpSolver()
solver.parameters.log_search_progress = True
solver.parameters.log_to_stdout = False
lines: list[str] = []
solver.log_callback = lines.append
solver.solve(model)

log = parse_log("\n".join(lines))

A log copied out of a terminal works just as well, including one that was clipped, prefixed by a logging framework, or has your own prints mixed in.

Why line anchors?

Log analyzers want to show the raw text next to the parsed data. Every scalar is a Loc[T] (value + line), table rows carry their line, every section has a span, and CpSatLog.blocks is an ordered index (kind, span, JSON pointer path) that maps any line back to the parsed section.

Supported versions

OR-Tools 9.3 to 9.15. Known format traps (older [Name] ... time= presolve lines, Solutions found per subsolver: lists, progress lines inside the presolve block, LRAT_status, renamed table columns) are handled. Every release is tested against the example logs in example_logs/ and against 295 real logs from public instance libraries in corpus/.

Extending

  • splitter.py cuts text into chunks; add forced cut points there when a new version glues sections together.
  • parsers/ holds one BlockParser per section; register new ones in parsers/__init__.py (first match wins).
  • parsers/tables.py::TABLE_IDS maps table titles to stable ids; add a line for a new table and give it a field in schema/tables.py::FinalStats.
  • assemble.py places blocks into the root model and merges multi-chunk sections.

Development

uv sync
uv run pytest
uv run ruff check . && uv run ty check

The CP-SAT Log Analyzer is built on this library: it explains a parsed log section by section in a web UI, and is hosted at https://cpsat-loganalyzer.krupke-algorithms.de/.

History

Version 1.0 is a rewrite. Up to 0.0.4 this package exposed a block-based API (LogParser, SolverBlock, SearchProgressBlock, ...) that returned loosely typed dictionaries; it is gone. The parser now produces one validated pydantic model per log, in which every value carries the line it was read from. Pin cpsat-logutils<1 if you depend on the old API.

License

MIT, see LICENSE.

Metadata

Release files for cpsat-logutils 1.0.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for cpsat-logutils 1.0.0
File Size Uploaded
cpsat_logutils-1.0.0.tar.gz 201.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for cpsat-logutils 1.0.0
File Interpreter ABI Platform
cpsat_logutils-1.0.0-py3-none-any.whl Python 3 none any Details

Total release size: 238.3 kB

Release files / cpsat_logutils-1.0.0.tar.gz

Download URL cpsat_logutils-1.0.0.tar.gz
Size 201.0 kB
Tags Source
SHA-256 checksum
How to use checksums
db8e0b5bf16fae203e08692088e9f8a248846e03934242367f7da5dab10322ee
BLAKE2b-256 checksum
How to use checksums
f8422c2c14746ce87ff2932c8e086efbcc264e05664f29e36934f176c4c8ca19
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release files / cpsat_logutils-1.0.0-py3-none-any.whl

Download URL cpsat_logutils-1.0.0-py3-none-any.whl
Size 37.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
02112cc460d30aeab3a591592bfa09cc5705fa3745d5d418a7339e8843c14e5d
BLAKE2b-256 checksum
How to use checksums
156e2c65834041468bcabb956c5e4465ad240af7f312b2d8b54d7c9a51b26885
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 6, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.0 This release

2 release files

0.0.4

2 release files

0.0.3

2 release files

0.0.2

2 release files

0.0.1

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page