Skip to main content

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 variant 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 / healed); the user-extensible VariantSource Protocol (competing readings of one observation).
  • 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), healed (absorbed under an alternative READING; d.variant_name names it, d.variant is the measurement actually used). 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.

Release files for gri-multitrack 0.3.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gri-multitrack 0.3.2
File Size Uploaded
gri_multitrack-0.3.2.tar.gz 75.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for gri-multitrack 0.3.2
File Interpreter ABI Platform
gri_multitrack-0.3.2-py3-none-any.whl Python 3 none any Details

Total release size: 132.2 kB

Release files / gri_multitrack-0.3.2.tar.gz

Download URL gri_multitrack-0.3.2.tar.gz
Size 75.5 kB
Tags Source
SHA-256 checksum
How to use checksums
0a298c868b6827123443793a4986d7098a044c683cac4436ae25a3aa46cf86ad
BLAKE2b-256 checksum
How to use checksums
7f218e125557d93535829a809bab5121a557d6c370ad68b51259ece35e92eeb0
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release files / gri_multitrack-0.3.2-py3-none-any.whl

Download URL gri_multitrack-0.3.2-py3-none-any.whl
Size 56.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0aaad823664f13326cef0fabca0c34407f0db83c2dc88ca489ead73e86cccef7
BLAKE2b-256 checksum
How to use checksums
c3e2ce08f6c99e2b94bd101f1481d09b6af570e4f1203e9516e5dad259472069
Upload date
Uploaded using Trusted Publishing?
What is 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}

Release history Release notifications | RSS feed

0.3.3

2 release files

This release

0.3.2 This release

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.1

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page