Files
gps-denied-onboard/_docs/02_document/common-helpers/03_helper_lightglue_runtime.md
T
Oleksandr Bezdieniezhnykh 4fdf1968af [autodev] Step 13 partial: helpers 1-4 cycle-1 doc sync
Batch 5a of the cycle-1 doc sync. For each of the four
foundation helpers (imu_preintegrator, se3_utils,
lightglue_runtime, wgs_converter):

- Append "Cycle-1 operational reality" section to the
  existing common-helpers/<NN>_*.md, documenting what the
  shipped implementation actually exposes vs. the design-
  intent sketch (interfaces, exception types, public
  constants, AZ-task lineage).

Specific cycle-1 facts captured per helper:

- imu_preintegrator (AZ-276): make_imu_preintegrator
  factory, BMI088-class noise defaults, single
  ImuPreintegrationError exception, actual return type is
  PreintegratedCombinedMeasurements (consumer builds the
  CombinedImuFactor), destructive reset_with_bias semantics,
  first-sample-not-integrated dt=0 handling.
- se3_utils (AZ-277): SE3 = gtsam.Pose3 re-export,
  Se3InvalidMatrixError, strict caller-orthogonalisation
  invariant, _DEFAULT_ROT_ATOL=1e-6 and small-angle Taylor
  cutoff for exp_map, is_valid_rotation predicate, strict
  dtype=float64 everywhere.
- lightglue_runtime (AZ-278 / R14 fix): EngineHandle
  Protocol-typed constructor, LightGlueRuntimeError +
  LightGlueConcurrentAccessError, non-blocking concurrent-
  access guard (raises rather than serialises),
  match_batch equal-length precondition, composition-root
  single-instance into C2.5 + C3.
- wgs_converter (AZ-279 + AZ-490): WEB_MERCATOR_MAX_LAT_DEG
  and MAX_ZOOM constants, WgsConversionError, ECEF arrays
  are ndarray(3,) float64, new horizontal_distance_m method
  (AZ-490 takeoff-origin bounded-delta gate), slippy-map
  tile math hand-rolled to match satellite-provider on-disk
  layout.

Two contract files (imu_preintegrator.md and
wgs_converter.md) need follow-up minor revisions to match
shipped surface; queued for the next contracts-folder
sweep, noted inline in each helper's new section.

Also refresh D-CROSS-CVE-1 opencv-pin leftover replay
timestamp (8-min debounce — gtsam upstream state cannot
change in that window).

Bumps _docs/_autodev_state.md sub_step detail.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-19 17:33:59 +03:00

3.9 KiB

Common Helper — LightGlueRuntime

Purpose

Shared LightGlue inference handle. C2.5 (Re-rank) does single-pair LightGlue matching for inlier counting on K=10 candidates per frame; C3 (CrossDomainMatcher) does the heavier matching pass on the surviving N=3 candidates. Both use the same LightGlue engine; sharing the engine avoids paying the engine-build / GPU-memory cost twice.

Used By

  • C2.5 — Inlier-based Re-rank.
  • C3 — Cross-domain Matcher.

Interface (sketch)

class LightGlueRuntime:
    def __init__(engine_handle: EngineHandle): ...
    def match(features_a: KeypointSet, features_b: KeypointSet) -> CorrespondenceSet
    def match_batch(features_a_list, features_b_list) -> list[CorrespondenceSet]
    def descriptor_dim() -> int

Implementation Notes

  • Owned by the composition root; the same instance is constructor-injected into both C2.5 and C3.
  • Backed by C7's InferenceRuntime.deserialize_engine(LIGHTGLUE_ENGINE_CACHE_ENTRY) at takeoff.
  • Single CUDA stream; concurrent calls forbidden — composition root binds the runtime to the single F3 hot-path thread.

Caveats

  • The features fed in MUST come from the same backbone as the LightGlue engine was trained for (DISK in production-default; ALIKED / XFeat in alternates). Mixing backbones is a runtime error caught by the matcher's input shape check.

Cycle-1 operational reality

The shipped surface in src/gps_denied_onboard/helpers/lightglue_runtime.py (AZ-278) is the structural fix for R14 (re-rank vs. matcher double-load of the LightGlue engine). Composition root wires ONE LightGlueRuntime instance and constructor-injects it into both C2.5 (InlierBasedReranker) and C3 (CrossDomainMatcher).

  • ConstructorLightGlueRuntime(engine_handle: EngineHandle). EngineHandle is a Protocol from _types/manifests.py (descriptor_dim, forward(...)) — Layer 1 helper invariant means we do NOT import gps_denied_onboard.components.*. C7's InferenceRuntime.deserialize_engine(LIGHTGLUE_ENGINE_CACHE_ENTRY) returns the concrete handle at takeoff; the composition root passes it in.
  • Construction guardsLightGlueRuntimeError is raised for: engine_handle is None; engine_handle missing the descriptor_dim Protocol attribute; descriptor_dim < 1.
  • Descriptor-dim mismatch — both match and match_batch validate every KeypointSet.descriptors against the engine's descriptor_dim and raise LightGlueRuntimeError on mismatch (catches "DISK features fed into an ALIKED-trained LightGlue" regressions).
  • Concurrent-access guard is non-blocking — the runtime owns a threading.Lock but never .acquire(blocking=True). Concurrent entry raises LightGlueConcurrentAccessError immediately rather than serialising. This is intentional: if you see this exception, the composition root wired the runtime into more than one thread by mistake — fix the composition, do NOT add blocking serialisation. The lock guards the body of both match and match_batch; descriptor_dim() is lock-free.
  • match_batch equal-length preconditionLightGlueRuntimeError if len(features_a_list) != len(features_b_list). Iteration uses zip(..., strict=True). Indexed validation labels (features_a_list[i]) so a downstream test failure points to the offending pair.
  • descriptor_dim() accessor — returns the engine's descriptor dim as a plain int (cached on construction so per-call overhead is one attribute lookup).
  • Public exceptionsLightGlueRuntimeError (construction / descriptor-dim mismatch / batch-length mismatch) and LightGlueConcurrentAccessError (composition-root violation). Both subclass RuntimeError.

Cycle-1 task lineage

  • AZ-278 — initial helper, contract producer.
  • R14 structural fix: composition-root single-instance injection into C2.5 + C3 lands in runtime_root/airborne_bootstrap.py (the lightglue_runtime pre-constructed key consumed by both _C2_5_STRATEGIES and _C3_STRATEGIES).