Files
gps-denied-onboard/_docs/02_document/contracts/shared_helpers/lightglue_runtime.md
T
Oleksandr Bezdieniezhnykh 880eabcb3f Decompose Step 6 snapshot: 140 task specs + contract docs
Closes out greenfield Step 6 (Decompose) for all 14 components
(C1-C13 + cross-cutting helpers/replay). Covers tasks AZ-266..AZ-446
plus the _dependencies_table.md and component contract documents.

State file updated to greenfield Step 7 (Implement), not_started.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 00:39:48 +03:00

6.6 KiB

Contract: lightglue_runtime

Component: shared_helpers / helpers.lightglue_runtime (cross-cutting concern owned by E-CC-HELPERS / AZ-264) Producer task: AZ-278 — _docs/02_tasks/todo/AZ-278_lightglue_runtime.md Consumer tasks: C2.5 InlierBasedReranker (single-pair LightGlue inlier counter); C3 CrossDomainMatcher (heavier matching pass) Version: 1.0.0 Status: draft Last Updated: 2026-05-10

Purpose

Single owner of the LightGlue inference engine. C2.5 does single-pair LightGlue matching for inlier counting on K=10 candidates per frame; C3 does the heavier matching pass on the surviving N=3 candidates. Both consume the SAME LightGlue engine — sharing avoids paying the engine-build / GPU-memory cost twice and structurally prevents the C2.5 ↔ C3 import cycle (R14 fix in _docs/02_document/epics.md). Per _docs/02_document/common-helpers/03_helper_lightglue_runtime.md.

Shape

For function / method APIs

class LightGlueRuntime:
    def __init__(self, engine_handle: EngineHandle) -> None: ...
    def descriptor_dim(self) -> int: ...
    def match(
        self,
        features_a: KeypointSet,
        features_b: KeypointSet,
    ) -> CorrespondenceSet: ...
    def match_batch(
        self,
        features_a_list: list[KeypointSet],
        features_b_list: list[KeypointSet],
    ) -> list[CorrespondenceSet]: ...
Name Signature Throws / Errors Blocking?
__init__ (engine_handle: EngineHandle) -> None LightGlueRuntimeError if engine_handle is None or descriptor_dim < 1 sync, one-time
descriptor_dim () -> int none sync, in-memory
match (KeypointSet, KeypointSet) -> CorrespondenceSet LightGlueRuntimeError if descriptor dims mismatch the engine's expected dim, or if a concurrent caller tries to enter sync, GPU-bound
match_batch (list[KeypointSet], list[KeypointSet]) -> list[CorrespondenceSet] same as match sync, GPU-bound

EngineHandle, KeypointSet, and CorrespondenceSet are imported from gps_denied_onboard._types. EngineHandle is a Protocol (NOT a concrete class) so this helper does not import any Layer 2+ component; the production handle is created by C7's InferenceRuntime.deserialize_engine and injected by the composition root.

Construction

The composition root constructs the runtime once at takeoff:

engine_handle = inference_runtime.deserialize_engine(LIGHTGLUE_ENGINE_CACHE_ENTRY)
runtime = LightGlueRuntime(engine_handle)
# inject the SAME instance into both consumers
c2_5_reranker = InlierBasedReranker(..., lightglue_runtime=runtime, ...)
c3_matcher = CrossDomainMatcher(..., lightglue_runtime=runtime, ...)

Invariants

  • Serial-access invariant (R14 cross-component): the runtime owns ONE CUDA stream. Concurrent calls to match / match_batch from multiple threads are FORBIDDEN. The composition root binds the runtime to the single F3 hot-path thread (per _docs/02_document/epics.md R14 entry). The helper's contract test asserts a guard exists that rejects concurrent entry with LightGlueConcurrentAccessError.
  • Backbone consistency: features fed in MUST come from the same backbone as the LightGlue engine was trained for (DISK in production-default; ALIKED / XFeat alternates). Mixing backbones is a runtime error caught by the input shape check (descriptor_dim mismatch raises LightGlueRuntimeError). The helper does NOT silently coerce dimensions.
  • No shared mutable state: the runtime exposes no set_* / update_* methods. Once constructed with an engine_handle, its behaviour is fixed for its lifetime.
  • No upward imports (Layer 1): the module imports ONLY from _types, numpy, and stdlib. NO gps_denied_onboard.components.* imports — neither C2.5 nor C3 nor C7 — under any circumstance. This is the structural fix for R14: the helper sits below the components in the layering, so the C2.5 ↔ C3 cycle becomes impossible to express.
  • Engine handle is opaque: the helper does not know whether the handle wraps a TensorRT engine, an ONNX session, or a PyTorch model. It calls a fixed Protocol surface (forward(...), descriptor_dim); the implementation owner is C7.

Non-Goals

  • Engine compilation / serialisation — owned by C7 (via EngineFilenameSchema + the inference runtime).
  • Engine cache management / takeoff load — owned by C10 (CacheProvisioner).
  • Backbone-specific feature extraction (DISK, ALIKED, XFeat) — owned by C3 / C7.
  • Multi-GPU sharding — out of scope; production target is single-GPU Tier-2.
  • Mixed-backbone matching (cross-DISK-ALIKED) — out of scope; consumers ensure backbone consistency before calling.

Versioning Rules

  • Breaking changes (method renamed/removed, signature changed, EngineHandle Protocol changed, serial-access invariant relaxed) require a new major version + a deprecation pass through C2.5 and C3.
  • Non-breaking additions (new optional kwarg with safe default, new diagnostic accessor) require a minor version bump.
  • Changing the underlying engine format (TensorRT → ONNX) is NOT a contract change because the helper's surface treats the handle as opaque — but it IS a C7 contract change and must follow C7's versioning rules.

Test Cases

Case Input Expected Notes
valid-single-pair two KeypointSets of matching descriptor dim CorrespondenceSet returned with len > 0 for a synthetic-overlap pair Round-trip happy path (C2.5 use)
valid-batch-3 three pairs of KeypointSets three CorrespondenceSets returned in order Batch path (C3 use)
invalid-dim-mismatch features with descriptor_dim not matching the engine LightGlueRuntimeError mentions the expected vs actual dim Backbone-consistency invariant
invalid-concurrent-access two threads call match simultaneously LightGlueConcurrentAccessError raised in the second-entering thread R14 serial-access invariant
invalid-empty-handle LightGlueRuntime(engine_handle=None) LightGlueRuntimeError raised at construction Construction guard
no-upward-imports static import scan only _types, numpy, stdlib — no components.* R14 structural fix
determinism-given-engine same (features_a, features_b) matched twice with the same engine handle byte-equal CorrespondenceSet outputs Pure-function determinism downstream of the engine

Change Log

Version Date Change Author
1.0.0 2026-05-10 Initial contract derived from _docs/02_document/common-helpers/03_helper_lightglue_runtime.md (R14 fix) autodev decompose Step 2