fix(qwen38flashnext): make layouts deterministic and use host services

The gap-fill pass stopped on a 120 ms stopwatch and QWEN_* environment
variables switched strategies, so the same job could nest differently
with machine load or environment. Gap fill now stops after eight failed
insertion sweeps, and the switches are internal properties with the
old defaults. Part reading, work area, rotation angles, scoring and
result assembly now use the host APIs; its collision gate is unchanged.

Synthetic benchmark (5 jobs, salvage 0.5): all valid, cost 7660.01 ->
7572.05, time 839 -> 553 ms. Not yet calibrated on production-size jobs.

Co-Authored-By: Codex <noreply@openai.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
aj
2026-09-25 09:29:28 -04:00
co-authored by Codex Claude Opus 5.5
parent 0308a862b8
commit 0cdef009f8
6 changed files with 98 additions and 329 deletions
+25 -6
View File
@@ -7,8 +7,7 @@ or select over any built-in engine, nester, filler, or runner.
## Algorithm
Bottom-left greedy insertion over convex No-Fit-Polygons, with an exact material-clearance
gate, driven sheet by sheet by a greedy demand scheduler. All geometry math is the engine's
own (`Engine/`); it calls no built-in nester, filler, or runner.
gate, driven sheet by sheet by a greedy demand scheduler. Placement and collision preparation remain in `Engine/`; it calls no built-in nester, filler, or runner.
- **`PartPreparation`** rebuilds each snapshot into a closed contour topology (perimeter +
cutouts; rapids/scribe marks dropped), flattens it circumscribed (the collision polygon
@@ -46,9 +45,9 @@ own (`Engine/`); it calls no built-in nester, filler, or runner.
- **`JobSolver`** walks demands in its own order (priority, then largest material area -
big parts first lay down the sheet skeleton the small parts fill against; measured 12%
lower job cost than smallest-extent-first on the production job below) and drains each greedily,
then a time-boxed gap-fill pass. For the next sheet it trials *every* available stock
size independently and commits the trial delivering the cheapest plate area per unit of
part area placed (the benchmark's cost function), breaking ties by priority coverage,
then a gap-fill pass capped at eight failed insertion sweeps. For the next sheet it trials *every* available stock
size independently and commits the trial delivering the cheapest `NestJobCost.NetSheetArea` per unit of
material area placed (the benchmark's cost function), breaking ties by priority coverage,
instance count, then plate area; lost trials change no job state. The job stops on met
demand, exhausted stock, no further placement, or the plate cap. Deterministic:
identical input, identical layout.
@@ -82,7 +81,7 @@ to ~110 s via the cached-triangulation exact gate and the fast shell prefilter.
## Tests
`tests/` holds acceptance tests whose layouts are checked by the benchmark's own
`NestValidator` (bounds, spacing, quantities, stock, rotation), plus NFP geometry tests and a
`NestLayoutCheck` through the shared `Engine.Testing` kit (bounds, spacing, quantities, stock, rotation and accounting), plus NFP geometry tests and a
rotated-concave spacing regression test.
```bash
@@ -107,3 +106,23 @@ dotnet <OpenNest>/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll <
`<OpenNest>` is the OpenNest checkout root. Or build and deploy in one step with
`./Build-Engines.ps1 -Engines Qwen38FlashNext`. The engine appears in reports as
`Qwen38FlashNextNestingEngine`.
## Shared services and determinism
`JobPartGeometry.TryRead` provides normalized material topology. Stock bounds/fit and
result accounting/progress use the shared stock API and `NestJobResultBuilder`.
`ForShape` supplies Automatic rotations; `EnumerateAngles(maxSamples: 4000)` preserves
the sweep effort cap. Solid parts use cached `DistinctOutlines`; holed parts retain every
legal candidate because perimeter symmetry alone cannot establish cutout symmetry.
CollisionTolerance remains 0.0005, below `NestTolerances.ValidationOutline`.
Gap fill now counts failed insertion sweeps (default eight), with successful insertions
bounded by demand. Internal test settings replace every QWEN environment switch. No
stopwatch affects placement or diagnostics. Only the host cancellation token limits wall
time. The shared determinism contract compares repeated and fresh solves.
On the five synthetic salvage jobs, every layout remained valid and complete; total cost
fell from 7660.01 to 7572.05, with no job worse. Aggregate measured solve time remained
below one second. These small fixtures do not calibrate production-scale retry costs;
the eight-sweep default bounds effort independently of hardware. See
[PR 5 results](../MIGRATION-PR5.md); older production numbers above describe the old version.