Skip to content

FloydWarshallOptimized: next matrix can contain a cycle for a reachable pair (root cause behind the v0.7.1 hang guard) #13

Description

@apotema

Background

v0.7.1 (#12) added a defensive cap to setPathWithMapping{,Unmanaged}: the next-hop reconstruction loop was infinite-looping because the next matrix contained a cycle that never reaches the goal, growing the path list unbounded → hard hang + OOM. Surfaced loading a 1000-worker flying-platform colony; a stack sample pinned it to getPath looping with ensureTotalCapacity climbing every iteration.

The cap makes reconstruction safe (returns NoPathFound past size hops), but it papers over the real defect:

The real question

A correct Floyd-Warshall next matrix must never contain a cycle for a pair where hasPath == true. next[u][v] is the first hop on a shortest path u→v; following it must strictly make progress toward v and terminate in ≤ size hops. A cycle means dist and next are mutually inconsistent — i.e. generate() produced a corrupt matrix, OR the input graph has a property the optimized variant mishandles.

Hypotheses to investigate

  1. Optimized (SIMD/parallel) generate() bug — the next update during relaxation is wrong/raced for some node ordering, producing a next that doesn't match the final dist. Compare FloydWarshallOptimized vs the scalar FloydWarshall on the same graph and diff the next matrices.
  2. Equal-cost / zero-weight edges — ties in relaxation (dist[i][k]+dist[k][j] == dist[i][j]) updating next inconsistently, creating a 2-cycle (next[u][v]=w, next[w][v]=u at equal cost).
  3. INF / disconnected handling — a pair marked reachable (hasPath) whose next chain routes through a wrongly-relaxed entry.
  4. Mapping layer (nextWithMapping/reverse_ids) — a stale/duplicate id mapping after the graph is rebuilt (the flying-platform graph is rebuilt on scene load), so the index→id translation yields a cycle even if the raw matrix is fine.

Repro lead

Deterministic: load big_colony (1000 workers) in flying-platform on the bgfx backend; the post-load repath storm hits the cyclic pair. Better: capture the exact (start, goal) node ids + the graph (edges) at the moment the guard fires, then reproduce in a zig-utils unit test by building that graph and asserting generate() yields an acyclic next.

Acceptance

  • A minimal failing case (graph + start/goal) reproduced in a zig-utils test.
  • Root cause identified (one of the above or new).
  • generate() fixed so next is always consistent with dist; the v0.7.1 cap stays as defense-in-depth.

Related: #12 (the guard), flying-platform#540 (pin bump).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions