Skip to content

Real-time inference optimisation

geobn includes two tiers of optimisation for repeated or latency-sensitive inference, such as continuously updating a dashboard as sensor values change.


The problem

By default, every call to bn.infer() fetches, aligns, and discretises all inputs, then runs one pgmpy VariableElimination query per unique evidence combination. For a 1000×1000 grid with terrain sources that never change, this is wasteful: DEM fetch + reprojection + discretisation can take several seconds even though the result is the same every time.


Tier 1 — Freeze static nodes

Use freeze() to mark nodes whose input data will not change between infer() calls:

bn.freeze("slope_angle", "aspect")        # declare terrain as static

result = bn.infer(query=["avalanche_risk"])
# First call: fetches terrain normally, caches the discrete index array.

bn.set_input("recent_snow", geobn.ConstantSource(35.0))
result = bn.infer(query=["avalanche_risk"])
# Second call: terrain array reused from cache — no fetch, no reprojection.

What is cached after the first call:

  • The discrete integer index array for every frozen node.
  • The reference GridSpec (so it is not re-derived from sources).
  • The pgmpy VariableElimination engine (constructed once, reused).

If the underlying data of a frozen node actually changes (e.g. you swap out the DEM source), call clear_cache() to discard the stale arrays before the next infer().


Tier 2 — Precompute all evidence combinations

When all inputs are effectively static and only a few dynamic scalar inputs change, pre-run the entire state-space combinatorial product once and store the results as a numpy lookup table:

bn.precompute(query=["avalanche_risk"])
# One-time cost: ∏ n_states_i pgmpy queries.
# For a 3×2×3×3 state space this is 54 queries (< 1 second).

result = bn.infer(query=["avalanche_risk"])
# Zero pgmpy calls — O(H×W) numpy fancy indexing only.

After precompute(), each infer() call replaces every pgmpy query with a single numpy index operation: the (H, W) discrete index grids are used directly to look up the pre-filled probability table.

Note

The table path activates automatically when infer() is called with the same query list that was passed to precompute(). Calling infer() with a different query falls back to the normal VE path.


Combining both tiers

The tiers stack naturally:

bn.freeze("slope_angle", "aspect")
bn.precompute(query=["avalanche_risk"])

# Subsequent infer() calls:
#   - Skip fetch/align/discretise for frozen terrain nodes (Tier 1)
#   - Skip all pgmpy queries (Tier 2)
result = bn.infer(query=["avalanche_risk"])

Cache lifecycle

Trigger Effect
bn.freeze(*nodes) called with a different node set clear_cache() is called automatically
bn.set_grid(...) Cache cleared automatically
bn.clear_cache() All caches discarded (frozen arrays, VE engine, inference table)

API reference

See the full method signatures in the GeoBayesianNetwork reference: