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.2.1.tar.gz (63.5 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.2.1-py3-none-any.whl (51.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: gri_multitrack-0.2.1.tar.gz
  • Upload date:
  • Size: 63.5 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.2.1.tar.gz
Algorithm Hash digest
SHA256 11492d22fc8927ce7fc0a840ed67bbc47b514414acbcf356249dab1c7a04606a
MD5 453e0bbc0e4e89ca4d3bd87ef4901e3e
BLAKE2b-256 bdfec391f16b3f2e7ff5d010eae947d0ebbc11cf6dd66c815be209924557e1c7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: gri_multitrack-0.2.1-py3-none-any.whl
  • Upload date:
  • Size: 51.3 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.2.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c641b63c5b28ed7afb86d1e46c3b47b5368a4186749113a47416c1918a47c16f
MD5 8a435a52834cf40dc837c6c0a745412c
BLAKE2b-256 0b5a357bf5afc905f9e0c5ecc4daca875aeecf19b9370b1b1847cf5f37afd061

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