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>
110 lines
6.7 KiB
Markdown
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`.
|