Skip to content

Public API

PyAIF supports the component-based constructor:

ActiveInfAgent(
    model=GenerativeModel(...),
    likelihood=CategoricalLikelihood(...),  # or ContinuousLikelihood(...)
    inference=ShallowInference(...),  # or a deep inference configuration
)

All three components are required together.

Stable components

GenerativeModel

Contains the domain-independent hidden-state dynamics:

  • B
  • D
  • controls_dim
  • controllable_factors
  • optional explicit policies

CategoricalLikelihood

Contains:

  • categorical likelihood tensors A
  • outcome preferences
  • modality-to-state-factor dependencies

ContinuousLikelihood

Contains:

  • a scalar density callback for each modality
  • one numerical integration grid per modality
  • individual or joint log preferences on those grids
  • modality-to-state-factor dependencies
  • an optional vectorized grid-density callback
  • optional likelihood-learning, preference-learning, and parameter-information-gain callbacks

ContinuousLikelihood.from_model(...) adapts a domain object that exposes likelihoods, get_o_grid, and log_preferences.

ShallowInference

Configuration:

  • message_passing_iterations
  • convergence_tolerance
  • policy_workers (default 1)

State and policy calls expose diagnostics through agent.last_state_inference and agent.last_policy_inference.

DeepTemporalInference

Configuration:

  • horizon
  • message_passing_iterations
  • convergence_tolerance
  • policy_workers (default 1)

Deep diagnostics include policy-dependent free energy, convergence, risk, ambiguity, information gain, and the final policy posterior.

Deep inference automatically batches policies with NumPy. Set policy_workers above one only after benchmarking a representative workload; small models are normally faster with the default single batched worker.

Lifecycle methods

Receding-horizon planning (0.3.0)

Use RecedingHorizonInference(horizon=3, message_passing_iterations=10) for a full planning window at every observation. It accepts the same configuration fields as DeepTemporalInference and supports categorical and continuous observations. Horizon counts state time points, including the current state; horizon=3 therefore evaluates two-action policies.

from PyAIF import ActiveInfAgent, RecedingHorizonInference

agent = ActiveInfAgent(
    model=model,
    likelihood=likelihood,
    inference=RecedingHorizonInference(horizon=3),
    action_selection="deterministic",
).reset()

for observation in observation_stream:
    agent.observe(observation)
    agent.infer_states()
    agent.infer_policies()
    action = agent.select_action()
    # Execute action and obtain the next observation.

Each decision reuses the existing policy-conditioned marginal message-passing solver on relative indices 0..horizon-1, scores the complete window using the existing policy-value convention, and selects policy action zero. There is no terminal-phase None action. The public clock advances once per selected action. At the next observation, the prior is B[action] @ q_current, where q_current is the policy-averaged belief at relative time zero, not a terminal prediction. The generative model's initial-state prior is preserved for subsequent resets.

If the controller executes a different action, pass agent.observe(next_observation, executed_action=actual_action). Supply one action index per hidden-state factor, including zero for uncontrolled factors. If omitted, PyAIF assumes the returned action was executed once. Delayed, partially executed, or continuous-duration controls need an application-specific transition model; this API represents one discrete transition per decision.

Follow the lifecycle once per decision. Do not call initialize_variables() between observations; window setup is automatic. step_time() is a compatibility no-op in this mode. Optional explicit time indices must match the current decision index. Adapters that constrain actions must examine policy row zero, not absolute_time % horizon. Custom policies must have exactly horizon - 1 rows. Time-dependent categorical preferences use relative window indices.

This mode uses policy-conditioned temporal inference, not a new Bayesian filter or a branching observation-contingent policy tree. Historical observations are summarized in the carried prior, rather than retained for fixed-lag smoothing. It is not guaranteed to be faster: all horizon points are evaluated every cycle and exhaustive policy enumeration still grows exponentially with action depth. Online parameter learning is currently rejected in this mode to avoid reusing overlapping evidence. Existing shallow and fixed-window learning are unchanged.

Run python examples/quickstart_receding.py for a complete example.

Filtered receding-horizon planning (0.4.0)

Use FilteredRecedingHorizonInference when the estimator and planner should be explicitly separated, as is common in online robotics:

from PyAIF import ActiveInfAgent, FilteredRecedingHorizonInference

agent = ActiveInfAgent(
    model=model,
    likelihood=likelihood,
    inference=FilteredRecedingHorizonInference(
        horizon=3,
        average_future_states=False,
    ),
    action_selection="deterministic",
).reset()

for observation in observation_stream:
    agent.observe(observation)
    agent.infer_states()  # current state only, once
    agent.infer_policies()  # future rollouts and EFE scoring
    action = agent.select_action()

infer_states() conditions a single factorized current belief on the newest observation and carried transition prior. It is independent of both the number and contents of the policy set. infer_policies() then places that same belief at relative time zero for every policy, predicts later beliefs through B, and uses the existing expected-free-energy terms to score relative times one through horizon - 1. The already-observed present is not scored as a future outcome. There are no future-to-present messages. Diagnostics are available as agent.last_state_inference and agent.last_policy_inference.

The filtered posterior is carried directly into the next action-conditioned prior. Policy-weighted averages of future state trajectories are skipped by default because they are not required for policy scoring or first-action selection. Set average_future_states=True to populate agent.bayesian_mod_avg[1:] for visualization or diagnostic consumers. The current entry, agent.bayesian_mod_avg[0], is always the filtered posterior.

This differs from RecedingHorizonInference, where marginal message passing jointly updates the complete policy-conditioned trajectory and future beliefs can influence the inferred present. Choose that mode when planning-as-inference semantics are important. Choose the filtered mode for a conventional filter-then-plan control loop, clearer estimator diagnostics, and state-update cost that does not scale with the number of policies. Future rollout and policy scoring still scale with policy count and horizon, so the filtered mode is not an unconditional latency guarantee.

Both receding modes use the same horizon convention, first-action execution, action-conditioned prior carry-over, categorical/continuous likelihoods, and optional parallel policy scoring. The executed_action rules and learning restriction described above also apply. Run python examples/quickstart_filtered_receding.py for a complete example.

Common lifecycle

  • reset(trial=0): normalize parameters and reset transient beliefs.
  • observe(observation, time_step=None): validate and store one multimodal observation.
  • infer_states(): update hidden-state beliefs.
  • infer_policies(): evaluate policies and update their posterior.
  • select_action(): select an action and advance the component lifecycle time.
  • learn(): apply enabled structural or likelihood parameter updates.

Learning

Learning is opt-in through learning_A, learning_B, learning_C, learning_D, and learning_E. The latest update summary is available as agent.last_learning.

Categorical likelihood parameters use the built-in Dirichlet learner. Continuous likelihood parameters are domain-dependent and use the ContinuousLikelihood.learning_fn callback. Structural B, D, and E learning remains available with continuous observations.

Compatibility layer

The legacy positional constructor remains available for current examples. It is not the recommended interface for new projects and may be deprecated after the examples complete their component-API migration.