Repository navigation
Add declarative readers for textual instance files - #270
Merged
Merged
Conversation
zayenz
force-pushed
the
feature/instance-parser
branch
3 times, most recently
from
October 29, 2026 14:45
fc158ee to
265e409
Compare
zayenz
marked this pull request as ready for review
October 30, 2026 06:01
zayenz
force-pushed
the
feature/instance-parser
branch
from
October 30, 2026 07:37
c8aef1a to
5a0da4c
Compare
zayenz
force-pushed
the
feature/instance-parser
branch
3 times, most recently
from
October 30, 2026 13:06
b0a9216 to
8ade15d
Compare
zayenz
force-pushed
the
feature/instance-parser
branch
from
October 31, 2026 06:55
8ade15d to
aea199f
Compare
zayenz
force-pushed
the
feature/instance-parser
branch
from
October 31, 2026 08:31
aea199f to
28ef29f
Compare
Add gecode/instance, a small library that reads textual benchmark instances with declarative reader specs supplied by the program. A spec is a short sequence of typed declarations and statements: values such as n: int, fixed-size and triangular arrays, lists that end at a sentinel or at the end of a line, line patterns with typed captures, keyed sections, repeated records, and a small if/let/check with integer arithmetic. The types are int, float, num, word, and rest, ranges such as 0..<n that check values as they are read, alphabets for character grids, and named types defined with type. An instances block reads files with several instances and keeps the one selected by number or name. Records are stored column-wise as flat typed arrays that may be ragged, built incrementally using Gecode heap storage rather than STL containers. Errors report the file, line, and column with the path through the spec; overflow, non-decimal numbers, values out of range, unknown parameters, and unknown functions are errors. The parser (spec.cpp), name resolution (resolve.cpp), expression evaluation (eval.cpp), and the reader (reader.cpp) are separate files. The gecode-instance tool reads a file with a spec file, prints a summary of the data or the data as MiniZinc data (ragged arrays as arrays of tuples), and lists the instances in a file. The module is wired into CMake, Autoconf, the package config, and the changelog and statistics scripts.
A script that reads instance files constructs InstanceOptions with the reader spec for its format. The option -spec then reads files with the spec in another file, -param gives values to spec parameters, and data() reads the instance once and returns its values. The driver library now requires the instance library.
…ogram examples Each example contains the reader spec for its format, with the ranges of the values it reads. SAT replaces its own parser, which assumed one clause per line. TSP reads TSPLIB files and computes their distances, TSPTW reads the López-Ibáñez and Blum collection, job shop reads OR-Library and JSPLIB files including a single instance from collections such as jobshop1.txt, the sudoku and advanced sudoku examples share a spec for the scaled sudoku corpus format with rectangular boxes, and nonogram reads the .non files exported by webpbn.com. All but SAT keep their built-in instances. A CI step runs each of these examples on a tiny file.
The generated-source check copied the tracked files with tar piped into tar. On macOS, bsdtar may stop reading at the end-of-archive marker before the writer has written its final padding; the writer then fails with a write error, which pipefail turns into a failed check. Whether this happens depends on the archive size, so the check failed in some checkouts only.
zayenz
force-pushed
the
feature/instance-parser
branch
from
October 31, 2026 09:09
28ef29f to
bd9f4d6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This adds
gecode/instance, a small library for reading textual benchmark instances. Each program describes its file format in a short declarative reader spec. The library reads a file with that spec, checks the values as it goes, and returns the data. Six examples now use it to read standard benchmark files, and a newgecode-instancetool prints the data in a file or converts it to MiniZinc data.This is part of 6.5.0:
feature/6.5.0-cleanupsis based on this branch.What a spec looks like
The SAT example reads DIMACS CNF with this spec:
Declarations put the name first and the type after it. A range such as
-vars..varsis checked as each value is read, so this spec needs no separate checks. The TSPTW spec also uses a named type, a repeat with named records, and checks:A check that uses a name read inside a repeat must hold for every record. When it fails, the error names the record and points at it in the input.
The language
int,float,num,word, andrest. Ranges are inclusive (lo..hi) or exclusive (lo..<hi), and either bound can be left out (0..). Alphabets read character grids (alphabet "123456789" blank ".").type name = ...defines a named type. The bounds can be any expression over values read earlier.d[n][n](withlayoutfor the TSPLIB triangular formats and an optionaloffset), lists that end at a sentinel or at the end of the line, and repeated records.line "p cnf {vars:int} {clauses:int}"),KEY : VALUElines and sections in any order (sections until "EOF" { ... }), and comment and stop markers. Insections, each arm must match exactly one line unless it is markedoptional, so a missing or repeated line is an error without a separate check.check,let, andifwith integer arithmetic (including^), comparisons, indexing (a[i], from 0),size,count,min,max,sum,distinct,defined, and comprehensions such asall(r in jobs: distinct(r.machine)). The constantsgecode.int_maxandgecode.int_minare available. Checks have a work budget proportional to the size of the input.instances { ... }block keeps the instance selected by number or name (-param instance=la01). A failed check, or a missing or repeated sections line, in another instance does not stop reading, andgecode-instance -listshows such failures.The full reference is in Doxygen, under
TaskInstanceSpec.Data and errors
Records are stored column-wise as flat typed arrays, which may be ragged. The
Instance::Dataaccessors take anIntArgspath of any depth, for exampled.ints("clause", {c}). Data storage uses Gecode heap memory rather than STL containers.Errors give the file, line, and column, the input line with a caret, the path through the spec, and the spec line:
Instance::Error.-spectakes over responsibility for the same checks.gecode-instanceexits with 2 for usage errors and 1 for spec or instance errors.Performance
The SAT example replaces a hand-written DIMACS parser, so I compared the two on random 3-SAT files (Release, macOS arm64):
A whole
satrun, stopped after the first search node, takes about 4.05 s on the 100 MB file, against 3.4 s on main. Peak memory there is 1.58 GB against 1.17 GB, because the parsed data stays in memory while the model is posted.Commits
Add the instance module. The library, the
gecode-instancetool, the tests, and CMake, Autoconf, and package wiring. The parser (spec.cpp), name resolution (resolve.cpp), expression evaluation (eval.cpp), and the reader (reader.cpp) are separate files. Without int variables, the module is left out of the build rather than turning int variables on. TheArrayTraits<ArgArray<double>>specialization moves into the kernel.Let
InstanceOptionsread instance files. A script passes its spec to the constructor:InstanceOptions opt("SAT", instance_spec).-spec FILEreads with another spec,-param name=valuesets a spec parameter (repeat the option, or separate pairs by commas, for several;gecode-instance -paramworks the same way),data()reads the file once, andbuiltin(i)tells a built-in instance number from a file name. The driver library now requires the instance library.Read instance files in the examples. All but SAT keep their built-in instances.
jobshop1.txtselected by name..nonfiles exported by webpbn.com.A new CI step runs each of these examples on a tiny file, so a mistake in an embedded spec fails CI.
Fix the freshness check on macOS. The generated-source check copied the source tree by piping
tarintotar. On macOS,bsdtarcan stop reading before the writer finishes, so the check failed in some checkouts depending on the archive size. It now copies through an archive file.The MPG documentation is in the section "Reading instance files" of the driver chapter, on the
Gecode/MPGbranchfeature/instance-reader.Size
Makefile.in, the smoke script, and the generatedconfigureMakefile.depchanges by about 13,900 lines because addinggecode/instance.hhto the existing rules rewraps them.Testing
On the final branch:
-test Instancepasses 40 of 40, also with ASan+UBSan, and the example smoke script passes. The first commit builds on its own and passes its 39 instance tests; the driver test is in the second commit. All 111 TSPLIB files of the mastqe/tsplib mirror were read; the 5 that fail use features outside the spec (FIXED_EDGES_SECTION, a free-textTYPE, more than 46340 nodes).-test Instancepasses 40 of 40 in both builds. Also run: the smoke script, 132 runs of the examples on built-in instances and small valid and invalid files, sudokus with rectangular boxes for every sudoku model, and parallel search with-threads 4. TSan reports nothing, and ASan and UBSan report no errors. LeakSanitizer reports leaks only in code that is the same on main: a static initializer intest/int/extensional.cpp, job shop's search stop and cutoff objects, andIntSets placed in aRegionin advanced sudoku.jobshop1.txt, all optimal.From the previous round, before the range types:
-tag checkpassed 516 of 516 with CMake on macOS, and Autoconf built and passed-test Instance. TSPLIB br17 gave 39, TSPTW n20w20.001 378, and sudoku and nonogram puzzles solved correctly. Autoconf and Windows run in CI.Known issue outside this PR
Since #233,
Search::Optionscontains aWorkerControlwhose destructor is in the search library. With Clang, every file that includessearch.hhemitsSearch::Builder's vtable, becauseBuilder::~Builderis defined inline. Through that vtable,~Options()pulls the search library's~WorkerControl()into libraries that do not linkgecodesearch. On Linux Debug builds,libgecodeint,libgecodeset, andlibgecodefloatare underlinked this way. This branch works around it by linkinggecode-instanceagainstgecodesearch.feature/6.5.0-cleanupsfixes the cause by definingBuilder::~Builderin the search library, and removes the workaround there.