Files
OpenNest-Engines/OpenNest.Engine.Qwen38FlashNext/README.md
T
ajandClaude Opus 5.5 90f07603e2 feat(qwen38flashnext): add the finished engine
Qwen3.8-Flash-Next's final version after a 14.5-hour optimization run
(its commit 7d7fca3): cost-first sheet trials, largest-area-first
demand order, and a cached-triangulation exact gate that brought a
219-part production job from timeout to ~106 s. 13/13 tests pass
against OpenNest master.

README cleaned for publishing: the model-facing template rules are
replaced by a one-line independence statement, the production job is
described generically instead of by its PEP job/file name (also in a
JobSolver comment), results show both sheet pools as re-measured here
(the 9-size claim in its report didn't reproduce: it grabs 96x240 and
under-fills them), and the stale StockLadder-crash note is gone now
that core leaves etch marks out of nesting.

Also drops a stale Aurora plugin reference from Opus55's README and
lists the engine in the repo README.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 06:57:11 -04:00

110 lines
6.7 KiB
Markdown

# OpenNest.Engine.Qwen38FlashNext
An independent whole-job `INestingEngine` built by Qwen3.8-Flash-Next: **bottom-left greedy
insertion over convex no-fit polygons with an exact clearance gate**. It does not call, wrap,
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.
- **`PartPreparation`** rebuilds each snapshot into a closed contour topology (perimeter +
cutouts; rapids/scribe marks dropped), flattens it circumscribed (the collision polygon
always contains the true material), and caches per-(part, angle, spacing) geometry: bounds,
convex hull, and the spacing-inflated outline **rotated into that orientation's frame**
(offset commutes with rotation; an unrotated inflation tests the candidate against the
material of a different angle - this was a real overlap bug, caught by
`RotatedConcavePartsKeepSpacingAtFixedAngles`). Candidate angles are the policy angles, or
0/90/180/270 plus the rotating-calipers minimum-bounding-rectangle angle for automatic
rotation.
- **`SheetPacker`** places one part instance at a time. Per already-placed part it builds a
convex NFP as `placedHull (+) disk(spacing) (+) reflect(candidateHull)` via its own
Minkowski edge-merge (`Convex.cs`; the merge picks the more-clockwise frontier edge, an
inverted comparison here corrupts every non-parallel sum into a self-intersecting contour),
then enumerates corner-point candidates: anchor work-box corners, NFP vertices, and
NFP-edge/box-line slides, tried in ascending bottom-left order. Because the NFP is
hull-based it only *certifies* clearance when both parts are convex solids with uncapped
hulls; everything else falls through to the exact gate - placed material inflated by the
spacing (holes shrunk, closed holes treated solid) versus the candidate's raw material with
holes subtracted, the same inflation rule the benchmark validator uses, so interlocking
concave parts are placed legally where the convex NFP alone would reject them. A uniform
spatial grid keeps the pair tests near-constant as the sheet fills, and an overlap memo
keyed by world pose collapses repeated clipper work across stock trials.
- **`FastPoly` / `CachedCollision` (`TriSet`)** make the exact gate cheap. Each orientation
caches flat-array triangulations of its raw material and its spacing-inflated gate
material; a candidate-vs-placed pair then runs the built-in clipper algorithm on plain
double arrays with the anchor offsets as translations - no `Polygon` clones, no
per-check re-triangulation, no LINQ. A uniform edge grid on the gate outlines certifies
disjoint shell pairs (clear) and convex-solid crossings (overlap) before any clip work;
the certification is three-state (touch and collinear contact defer to the clip,
containment is decided by sampled interior tests) so it can never report a false clear -
cross-validated against `Collision.HasOverlap` over ~2.5M decisions per job run with
zero verdict mismatches, and hole-clipping overflow falls back to the exact `Polygon`
gate (0.2% of checks on the production job below).
- **`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,
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.
Trade-offs: greedy BLFG insertion leaves some of the density interlocking-pair and
compaction pipelines find on regular jobs, and every stock size is trialled per sheet
(O(sheets x stocks x fill)); on the 69-drawing/219-part production job below that costs
~110 s against the benchmark's 5-minute per-solve timeout. In exchange it places arcs,
concaves, and holed parts under one uniform gate with no per-shape-class special cases.
## Benchmark results
A real laser-cutting production job: 69 drawings, 219 parts, 3/16 mild steel, spacing 0.3,
`--parallel 1`, same OpenNest build for every engine.
| Sheet sizes offered | Result | Sheets | Utilization | Cost | Time |
|---|---|---|---|---|---|
| The job's own 4 sizes (60x96, 60x120, 72x120, 48x144) | valid, 219/219 | 28 | 78.4% | 219,744 | ~106 s |
| OpenNest's standard 9-size catalog | valid, 219/219 | 14 | 56.6% | 304,128 | ~132 s |
It uses the fewest sheets of any engine tested, but not the least material. The shop's
original hand layout used 29 sheets (191,232 sq in). **Known weakness:** sheet choice is
greedy one sheet at a time, so with large stock available it grabs 96x240 sheets and
under-fills them.
Optimization history on this job (all valid, 219/219): count-first trial scoring and
span-first demand order cost 258048/39 plates; cost-first trial scoring brought it to
249696 (39); area-first demand order to 219744 (28). Wall time went from timeout (>400 s)
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
rotated-concave spacing regression test.
```bash
dotnet test OpenNest.Engine.Qwen38FlashNext/tests/OpenNest.Engine.Qwen38FlashNext.Tests.csproj
```
## Build and benchmark
The project is a plugin outside `OpenNest.sln`. `OpenNest.Benchmark` loads plugin engines
from an `Engines/` folder next to its own build output:
```bash
dotnet build OpenNest.Engine.Qwen38FlashNext/OpenNest.Engine.Qwen38FlashNext.csproj -c Release
dotnet build <OpenNest>/OpenNest.Benchmark/OpenNest.Benchmark.csproj -c Release
mkdir -p <OpenNest>/OpenNest.Benchmark/bin/Release/net8.0/Engines
cp OpenNest.Engine.Qwen38FlashNext/bin/Release/net8.0/OpenNest.Engine.Qwen38FlashNext.dll <OpenNest>/OpenNest.Benchmark/bin/Release/net8.0/Engines/
dotnet <OpenNest>/OpenNest.Benchmark/bin/Release/net8.0/OpenNest.Benchmark.dll <path-to-.nest-or-folder> --parallel 1
```
`<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`.