Skip to content

Use a table for TSPTW travel times - #271

Merged
zayenz merged 1 commit into
mainfrom
feature/tsptw-tupleset
Oct 30, 2026
Merged

zayenz merged 1 commit into
mainfrom
feature/tsptw-tupleset

Conversation

@zayenz

@zayenz zayenz commented Oct 30, 2026

Copy link
Copy Markdown
Member

The TSPTW example ran very slowly under the experimental A* and CABS engines: on a generated 60-node instance CABS explored about 100 nodes/s, while BAB managed about 80,000.

Root cause

The engines are fine; the problem is the model. The travel time constraint element(d, x[i], n, x[i-1], n, c[i]) is posted as an index-pair variable over all n² pairs plus an Element::Int over the flattened matrix. Its clone constructor drops the index/value lists, so the first propagation in every clone rebuilds and sorts them. Fixing one customer removes a value from every other x[j] through the channel, which wakes all ~n of these propagators and makes each rescan ~n² entries. A* and CABS clone and propagate every child of every node they expand near the root, so they pay this cost on every node, while BAB mostly works deep in the tree where domains are small. At n=60, CABS spent 2.97 s of a 3 s run in status() on children at depth 1–6, against 17 ms spent cloning.

Change

  • examples/tsptw.cpp: replace the matrix element with one TupleSet over (x[i-1], x[i], c[i]), shared by all positions, and post it with extensional.
  • gecode/search.hh: document in HeuristicGuidance that a node costs a clone plus propagation at shallow depth, so models should use propagators that stay cheap there. Also document that without a dominance_key, every node offered to the beam is compared with every node in it.

No engine changes.

Measurements

Built-in instances, complete search, median of 20 runs (-mode time -samples 20), RelWithDebInfo:

Instance Engine Optimum (main / PR) Nodes (main / PR) Time (main / PR)
0 (rbg010a) BAB 671 / 671 3911 / 3899 32.6 / 20.9 ms
0 A* 671 / 671 3911 / 3883 25.7 / 16.4 ms
0 CABS 671 / 671 2564 / 2608 28.9 / 16.5 ms
1 (n20w20.001) BAB 378 / 378 527 / 522 48.2 / 23.0 ms
1 A* 378 / 378 527 / 522 40.5 / 21.1 ms
1 CABS 378 / 378 923 / 905 83.0 / 40.1 ms

Generated instances with n customers, nodes explored in 3 s. These were measured with a local file-reader hack that is not part of this PR:

Engine n=20 (main / PR) n=40 (main / PR) n=60 (main / PR)
BAB 208k / 340k 180k / 227k 245k / 292k
A* 24.1k / 159k 1.7k / 55.9k 278 / 23.5k
CABS 39.2k / 183k 3.0k / 79.0k 301 / 35.0k

Validation

  • Built out of tree with -DGECODE_ENABLE_EXPERIMENTAL=ON, next to a build of origin/main.
  • BAB, A* and CABS find the same optimum on both built-in instances as on main (671, 378), and each search runs to completion.
  • gecode-test -test ^Search: 25815 passed, 0 failed.
  • git diff --check is clean.

The matrix element posted one index over all n*n customer pairs per
position, and its propagator rebuilds its support lists in every clone.
A* and CABS clone and propagate every child near the root, so this
dominated their runtime. Use one shared TupleSet over (x[i-1], x[i],
c[i]) instead, and note the per-node cost and unkeyed dominance in the
heuristic guidance documentation.
@zayenz
zayenz force-pushed the feature/tsptw-tupleset branch from af7e5b1 to 84e20b6 Compare October 30, 2026 09:07
@zayenz
zayenz marked this pull request as ready for review October 30, 2026 09:09
@zayenz
zayenz merged commit b89fcf4 into main Oct 30, 2026
16 checks passed
@zayenz
zayenz deleted the feature/tsptw-tupleset branch October 30, 2026 09:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant