Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 31 additions & 10 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ byte-reproducible** — with **our own placement optimizer** on top.

**Read first:** [`docs/00-ironnest-architecture-and-plan.md`](docs/00-ironnest-architecture-and-plan.md)
(the plan), [`docs/01-jagua-source-verification.md`](docs/01-jagua-source-verification.md) (the
source-verified fork grounding), and [`docs/02-optimizer-and-separation-search.md`](docs/02-optimizer-and-separation-search.md)
(the optimizer design + the next build: the separation search). They are the source of truth; this
file is the short version.
source-verified fork grounding), [`docs/02-optimizer-and-separation-search.md`](docs/02-optimizer-and-separation-search.md)
(the optimizer design + measured results), and [`docs/03-nfp-construction-mode.md`](docs/03-nfp-construction-mode.md)
(the designed-but-not-built exact-NFP construction mode — feature #4). They are the source of
truth; this file is the short version.

**Status (2026-06-21):** Phase 1 (fork→f64) ✅ · Phase 2a (constructive+compaction nester, 92–99% on
rectangular parts) ✅ · Phase 2b (sparrow GLS **separation search**, `crates/optimizer/src/sep/`;
Expand All @@ -20,8 +21,26 @@ gate on macOS-arm64/Win-x64/linux) ✅ · Phase 4 (**PyO3 `ironnest` wheel** via
Phase 6 (**interior-void holes + multi-sheet**: `nest(holes…)` keep-out zones = "nesting inside parts",
`nest_multi(sheets…)`; bound in the wheel — `docs/00` §10) ✅ · Determinism-hardening (**risk #2
resolved**: vendored libm-deterministic min-sep offsetter in `crates/geo/src/buffer/`; dropped
`geo`/`atomic-polyfill`; min_sep>0 now x-platform byte-identical) ✅ · **Next: Phase 5 — consumer
integration** (`drawing_and_gcode` #258). See `docs/00` §10.
`geo`/`atomic-polyfill`; min_sep>0 now x-platform byte-identical) ✅ · Optimizer round 3 (2026-07-09,
`docs/02` §11.2): evaluator **early-terminate** (~13 % faster separation, byte-identical proof) +
multi-start **order diversification** (parity split; +1 pp on diameter-misleading mixes) ✅; wall
compression measured → reverted (insertion-driven separation subsumes it — don't re-try) ·
**Feature #4 (exact-NFP construction, `docs/03`): N0 GO → N1a ✅ → N1 ✅ (2026-07-10)** —
N0 `examples/nfp_spike.rs`: mixed-13 consumer corpus 79.7 % in 0.94 s construction-only (full
sampling K=1 pipeline: 80–83 % in 150–214 s); contact-columns 100/100; interlock by construction.
N1a: import hoist (`Problem`/`import_problem`), goldens byte-identical. N1: integer substrate in
`crates/optimizer/src/nfp/` (doc-hidden pub) — 2^20 grid/i64/i128, convex edge-merge fast path,
orientation-normalized convolution union-fill, **quads-only face-classified IFP** (P1 caught the
concave-pocket leak of the naive frame seed — docs/03 §11.2), □E=16 backoff, eager mirror-derived
pair cache, domain gate; `i_overlay = 7.0.2` exact-pinned (integer i64 API only). N2 ✅: placer +
`PlacementStrategy`/`NestConfig`/`NestError`/`nest_with_config` (+`nest_multi_with_config`), 6 NFP
goldens blessed. N3 ✅: block-split policy wins the NFP A/B (94.01 vs 93.49 %); `PartsGeometry`
shared across sheets; `nfp-multistart` golden (jittered winner) incl. `--features parallel`.
N4 ✅ core: wheel ships `strategy="sampling"|"nfp"` on both pyfunctions; **ACCEPTANCE = SHIP
OPT-IN** — consumer corpus K=16 paired: NFP 83.88 % vs sampling 83.27 % mean (+0.61 pp, floor held
every seed, 1.02× clock, 84.03 % all-time best). Follow-ups: consumer adapter `strategy=` +
`placement_mode` audit key (`drawing_and_gcode` #258), ESICUP tracker, rejection-counter
surfacing, post-sep NFP refill (the GO lever). See `docs/03`.

## ⛔ Two prime directives

Expand Down Expand Up @@ -64,9 +83,10 @@ import-only-then-resorted, `Instant` is metadata). Keep it that way. The verifie
- **No `rayon` / threads on any placement-deciding path.** Single canonical worker; if parallel, fix
the reduction order. (Import-time `par_iter` is fine — it's re-sorted by id.) **The one sanctioned
exception:** `nest_multistart`'s `parallel` feature runs the K independent best-of-K starts on
`std::thread::scope` threads — each start is the self-contained, golden-stable `nest_per_item` (own
PRNG/`Layout`, no shared state), results join in fixed k order, and the keep-best `total_cmp` argmax
runs after all joins, so no float reduction crosses a thread. Never thread *inside* a single
`std::thread::scope` threads — each start is the self-contained, golden-stable `multistart_start`
(own PRNG/`Layout`, no shared state; per-start seed + insertion-order policy have a single
definition shared with the sequential build), results join in fixed k order, and the keep-best
`total_cmp` argmax runs after all joins, so no float reduction crosses a thread. Never thread *inside* a single
placement search. Proven by the golden re-run under `--features parallel` (CI, all 3 platforms).
- **`BTreeMap`/`BTreeSet`/`slotmap` only** where order affects placement. Never std `HashMap`
(per-process random iteration).
Expand All @@ -76,8 +96,9 @@ import-only-then-resorted, `Instant` is metadata). Keep it that way. The verifie
## The fork

- Upstream: **jagua-rs 0.7.2** (commit `43e8137`), MPL-2.0, Rust **edition 2024**. Edition 2024 needs
Rust ≥ 1.85, but jagua's code uses `{int}::cast_signed/cast_unsigned` (stable 1.87) → **our MSRV
floor is 1.87** (toolchain pinned to 1.96). All deps pure-Rust (no C / no `build.rs`) → clean wheels.
Rust ≥ 1.85; jagua's code uses `{int}::cast_signed/cast_unsigned` (stable 1.87) and `i_overlay`
7.x needs 1.88 → **our MSRV floor is 1.88** (toolchain pinned to 1.96). All deps pure-Rust (no C /
no `build.rs`) → clean wheels.
- **Fork ≈ 5,800 LOC** (see `docs/01` §B): all of `geometry/` (ex-SVG) + `collision_detection/` +
`entities/` + `io/import.rs`+`ext_repr.rs`+`export.rs` + `probs/bpp/` + `util/fpa.rs`. **Skip**
`io/svg/`, `probs/spp`+`mspp`, and the `lbf` binary (read `lbf` only for the
Expand Down
57 changes: 50 additions & 7 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ resolver = "3"
members = ["crates/geo", "crates/cde", "crates/optimizer", "crates/ironnest", "crates/py"]

[workspace.package]
version = "0.4.1" # bump deliberately; re-bless goldens if placement code changed
version = "0.5.0" # bump deliberately; re-bless goldens if placement code changed
edition = "2024" # jagua-rs lineage; edition 2024 needs Rust ≥ 1.85
rust-version = "1.87" # MSRV floor: jagua's forked code uses {integer}::cast_signed/cast_unsigned
rust-version = "1.88" # MSRV floor: i_overlay 7.x (docs/03 §3.5); jagua's cast_signed floor was 1.87
# (stable 1.87). We build on the pinned 1.96.0 — see rust-toolchain.toml.
license = "MPL-2.0" # uniform across the repo

Expand Down
7 changes: 4 additions & 3 deletions crates/ironnest/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@

#[doc(inline)]
pub use ironnest_optimizer::{
MultiSheetSolution, NestSolution, Placement, Scalar, Sheet, nest, nest_multi,
nest_multi_multistart, nest_multi_multistart_per_item, nest_multi_per_item, nest_multistart,
nest_multistart_per_item, nest_per_item,
MultiSheetSolution, NestConfig, NestError, NestSolution, Placement, PlacementStrategy, Scalar,
Sheet, nest, nest_multi, nest_multi_multistart, nest_multi_multistart_per_item,
nest_multi_per_item, nest_multi_with_config, nest_multistart, nest_multistart_per_item,
nest_per_item, nest_with_config,
};
1 change: 1 addition & 0 deletions crates/optimizer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ parallel = []
ironnest-geo = { workspace = true }
ironnest-cde = { workspace = true }
slotmap = "1.0" # deterministic keyed storage — `SecondaryMap` for the separation tracker (matches cde)
i_overlay = "=7.0.2"
# Deliberately NO `rand`/`rand_pcg`: the placement PRNG is a small, self-contained PCG64 (see
# `prng.rs`) so the exact bit-stream is version-independent and byte-stable forever — determinism is
# the product. No threads, no wall-clock, no external RNG.
Expand Down
Loading
Loading