Skip to content

Boolean results drift run-to-run on identical input (ordering, not topology) #108

Description

@GeneralPawz

The determinism probe in the benchmarks repo reports run-to-run
drift on identical input, reproducibly, at 411fa0e:

axiolid subtract_many (grouped)  verts=136  tris=332  NONDETERMINISTIC (20 distinct)
axiolid single boolean           verts=16   tris=32   NONDETERMINISTIC (2 distinct)
raw boolmesh (sequential)        verts=136  tris=332  STABLE
raw boolmesh (FUSED tool)        verts=136  tris=332  NONDETERMINISTIC (14 distinct)
cellular (analytic, opt-in)      verts=272  tris=604  STABLE

What it is and is not

Vertex and triangle COUNTS are identical every run; only the
geometry hash differs. So this is ordering/value drift, not
unstable topology. Volume and the exactness identities still
pass, which is why it went unnoticed.

It is not cosmetic: it defeats build reproducibility and content
hashing, and it is the likely cause of the original IfcConvert
symptom (9664 vs 9665 verts across identical runs) that the
probe was written to isolate.

Hypotheses already eliminated

Tested and ruled OUT, so the next person does not repeat them:

  • HashMap iteration order in boolean45.rs. Converted all
    three maps (pt_p, pt_q, pt_new) to BTreeMap and re-ran:
    still nondeterministic (14 -> 12 distinct is noise, not a fix).
    Reverted rather than kept, since it did not earn its change.
  • Threading. The parallel feature is OFF by default and the
    STABLE sequential row uses the same build, so rayon is not it.
  • The remaining HashMaps in manifold.rs/dedup.rs are
    lookup-only, never iterated, so they cannot leak hash order.

Where the evidence points

Sequential raw boolmesh is STABLE while the FUSED-tool variant of
the SAME operation is not, and upstream crates.io boolmesh 0.1.9
shows it too. That isolates the drift to the multi-component
operand path rather than to anything absorbed locally.

Suggested next step: hash intermediate stages inside one solve
(post-classification, post-triangulation, post-simplification) to
find the first stage where two runs diverge, instead of guessing
at containers.

Reproduce: cargo run --release -- --only=determinism in the
benchmarks repo.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

needs-triageFiled but not yet accepted, sized, or scheduled

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions