Skip to main content

Multi-target tracking over gri per-target estimators: ingest/routing, measurement-space scoring, GNN + MFA/MHT association, track lifecycle, LMB existence, and Poisson-binomial cardinality

Project description

gri-multitrack

Multi-target geolocation tracking. gri-multitrack combines the gri per-target Kalman-IMM (gri-kalman, fed by gri-obs observables) with a multi-target policy layer: ingest/routing of the feed-in data tiers, measurement-space scoring, per-scan association, track lifecycle, existence (Labeled Multi-Bernoulli), and a Poisson-binomial count distribution.

The governing seam is "gri scores, gri-multitrack assigns": gri-kalman owns per-target estimation and the measurement-space likelihoods; gri-multitrack owns everything multi-target. The multi-target layer is DIY on numpy/scipy -- gri-multitrack is Stone-Soup-free (see the "Architecture pivot" in AGENTS.md). The primary tracker class is MultiTracker (the "Crucible" name now belongs to the companion 3D app).

See PLAN.md for the live program plan (state, backlog, decisions) and AGENTS.md for repo guidance. Scenario generation, replay harnesses, scoring, and the replay viewer live in the sibling gri-tracksim repo, which depends on this engine and serializes its outputs (the engine itself is serialization-free).

Status

v1 of the original build plan is complete (see PLAN.md): ingest -> score -> associate -> gri IMM update -> lifecycle -> LMB output, on geos, raw TDOAs, and presence events, plus the outlier stream (clutter floors, per-observation dispositions, the extensible cure catalog), the stationary convolve resolver, split/merge with lineage, and batch RTS retrospectives.

Implemented:

  • Ingest + router for the feed-in tiers (geo Ell, observables, presence).
  • Per-track adapter over any gri Tracker (default SmartSegmentedIMM; CV / CoordinatedTurn / Static bank).
  • Measurement-space scoring seam (Gaussian innovation + chi-squared gate).
  • GNN scaffold associator (local scipy Hungarian) for bring-up.
  • MFA tracker (MfaTracker): a local hypothesis-oriented MHT that defers at ambiguous crossings and resolves via accumulated kinematic likelihood -- the v1 associator (loose-coupled; not Stone Soup's MFA). Standalone engine mirroring MultiTracker.
  • Track lifecycle: birth from geos, M-of-N confirm, patient deletion.
  • Existence r_i + Poisson-binomial count distribution.
  • Presence ("is it on") -> coast(t) + existence bump.
  • LMB output: labeled tracks + count distribution + top-level per-observation dispositions + the per-scan association diagnostic (gates / marginals / hypotheses), associator-agnostic. See Output.

Also implemented since the skeleton:

  • Per-kind clutter likelihood floors and per-observation DISPOSITIONS (assigned / birthed / clutter / cured); the user-extensible Cure Protocol.
  • The stationary resolver (convolve as the live estimator of a locked track; the cluster answer as resolved), split/merge with TrackEvent lineage, and smoothed_tracks() batch retrospectives.
  • Serialization, GOSPA/OSPA metrics, and the replay viewer live in gri-tracksim (the engine stays serialization-free).

Notable design choices

  • The per-track estimator is any gri-kalman Tracker; the default is SmartSegmentedIMM. gri-kalman exposes one uniform interface (update(ell, t) / update_observable / predict / coast / smoothed_track / result / is_initialized) across IMM, SmartIMM, SegmentedIMM, and SmartSegmentedIMM. gri-multitrack defaults to the maneuver-segmenting, outlier-rejecting SmartSegmentedIMM the design calls for; pass tracker_factory=make_imm (or any Tracker factory) to swap it. The choice is isolated to gri_multitrack/track.py.
  • Stone-Soup-free; multi-target is DIY on numpy/scipy. Both associators are local (GNN over scipy linear_sum_assignment; MFA a local hypothesis-oriented MHT). Stone Soup's MFA is filter-coupled and would cost the gri IMM, and is heavy (~48 MB of deps + ortools); see the CLAUDE.md "Architecture pivot". If LAP speed ever matters, add lapsolver/lap (tiny) -- not ortools. Stone Soup remains only as an optional dev-time GOSPA/OSPA cross-check.

Install

Uses uv with editable path dependencies on the sibling gri repos (in ../../foss/).

uv sync                     # core (Stone-Soup-free)
uv sync --extra crosscheck  # optional: Stone Soup, for a dev-time GOSPA/OSPA check only

Run

uv run python examples/two_target_demo.py   # end-to-end demo
uv run pytest                                # tests
uv run ruff check gri_multitrack test              # lint
uv run ty check                              # type check

Quick use

from gri_multitrack import MultiTracker

tracker = MultiTracker()
# each item is (payload, time_s); payload is an Ell, a gri-obs observable,
# or a PresenceObs.
outputs = tracker.process([(ell0, 0.0), (tdoa1, 1.0), (presence, 2.0)])

final = outputs[-1]
for t in final.tracks:
    print(t.label, t.existence, t.is_stationary, t.mode_probabilities)
print(final.count_distribution)  # Poisson-binomial P(N=k)

Output

A TrackerOutput per scan, in the same unified surface a single-target gri-kalman tracker reports (a single-target tracker is the degenerate one-track case), so the two are read interchangeably.

  • output.tracksLabeledTrack records (a gri-kalman TrackEstimate plus the stationary fields). Each carries state as an EllVel (position + velocity + 6x6 covariance; .ell for the position-only Ell), mode_probabilities, existence (r_i), confirmed, hits, parent (split lineage), is_stationary / stationary_locked / resolved (the convolver's cluster answer), and a bound predict.

  • Where are the unused / outlier observations? Top-level output.dispositions, one Disposition per observation. Each has index, used, verdict, track, confidence. The outlier bucket is:

    outliers = [d for d in output.dispositions if not d.used]
    

    Verdicts: assigned (absorbed by a track), birthed (seeded a new track), clutter (explained better as clutter — used=False), cured (repaired by a Cure then absorbed; d.cure names it, d.variant is the repaired measurement). The committal GNN reports confidence=1.0; the MFA reports the world-agreement mass with provisional=True.

  • output.count_distribution / expected_count / most_likely_count — the Poisson-binomial cardinality over the existences.

  • output.events — this scan's split / merge TrackEvents.

  • output.association — the per-scan diagnostic (gates / marginals / hypotheses); None when not computed.

  • output.worlds — the MFA's surviving global hypotheses with weights (the MHT-only confidence surface); empty for the committal GNN.

  • Prediction: track.predict(dt_s) returns a PredictedState at any horizon (a locked-stationary track predicts its convolved fix). Smoothing is opt-in: tracker.smoothed_tracks() returns the per-label RTS retrospective (best given all data, refining the past); the live tracks are the filtered best-given-data-so-far.

Layout

  • gri_multitrack/ingest.py -- feed-in types, routing, scan grouping.
  • gri_multitrack/track.py -- per-track adapter over the gri IMM.
  • gri_multitrack/scoring.py -- measurement-space likelihood + gate ("gri scores").
  • gri_multitrack/association.py -- GNN scaffold + Associator protocol.
  • gri_multitrack/lifecycle.py -- birth / confirm / delete / existence.
  • gri_multitrack/cardinality.py -- Poisson-binomial count distribution.
  • gri_multitrack/output.py -- Labeled Multi-Bernoulli output records.
  • gri_multitrack/tracker.py -- the MultiTracker orchestrator.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gri_multitrack-0.3.0.tar.gz (65.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gri_multitrack-0.3.0-py3-none-any.whl (51.6 kB view details)

Uploaded Python 3

File details

Details for the file gri_multitrack-0.3.0.tar.gz.

File metadata

  • Download URL: gri_multitrack-0.3.0.tar.gz
  • Upload date:
  • Size: 65.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for gri_multitrack-0.3.0.tar.gz
Algorithm Hash digest
SHA256 96827f2a1cd791563888aae31d93b5b4ce9f517d7c7e012f168e86a680596ba1
MD5 bcbdd0bebd09d4e1773eaa71143932c8
BLAKE2b-256 33f6b455afccd4164bf38386b97e2325275c8cb19ed230dd69e00512c841abdf

See more details on using hashes here.

File details

Details for the file gri_multitrack-0.3.0-py3-none-any.whl.

File metadata

  • Download URL: gri_multitrack-0.3.0-py3-none-any.whl
  • Upload date:
  • Size: 51.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.9.30 {"installer":{"name":"uv","version":"0.9.30","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Debian GNU/Linux","version":"12","id":"bookworm","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for gri_multitrack-0.3.0-py3-none-any.whl
Algorithm Hash digest
SHA256 297e8ea39585683223f90a357b0d7125a98568c6e49b0ee9804676bf09991fa8
MD5 2963af654d3e54a7a5abd9365391e82b
BLAKE2b-256 80be76aa9d59e9813e0caf0d0949c308308757cf06404ca7fb8a9611da819df8

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page