ReactiveBayes / ReactiveBayes/RxInferExamples.jl

Autoregressive Active Inference: sign conventions, unreachable rules, and an EFE-landscape figure mismatch

Open
#93 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Jupyter Notebook
Stars
28
Forks
14
Avg merge
23h 47m
Merged PRs (30d)
10

Description

While optimising the Advanced Examples/Autoregressive Active Inference notebook (PR #94) I ran into a few things in the message rules that I deliberately did not change, because they look surprising but may well be intentional. Flagging them for @wmkouw rather than "fixing" them.

Everything below was measured on Random.seed!(3), 400 steps, RxInfer 5.5.2 / ReactiveMP 6.4.0.

1. Two sign conventions that cancel (not a bug — but please add a comment)

Two places look mutually inconsistent:

  • prod(::ClosedProd, ::MvLocationScaleT, ::unBoltzmann) uses Q(y) = -logpdf(left, y) - right.G(y), whereas the analogous prod(::ClosedProd, ::AbstractMvNormal, ::unBoltzmann) uses + right.G(u). Since logpdf(::unBoltzmann) = -G, the product's negative log density "should" be -logpdf(left) + right.G.
  • The :outprev1 rules return G(outprev1) = logpdf(marx_predictive(...), m_star) — a positive log density as an energy, where -logpdf would be conventional.

They turn out to be a compensating pair. Flipping both is bit-identical to flipping neither; flipping either one alone breaks the agent completely:

prod sign :outprev1 sign waypoints reached
- (current) + (current) 3/3 — switches at [103, 129, 144]
+ + 0/3
- - 0/3
+ - 3/3 — switches at [103, 129, 144]

So the code is self-consistent and correct as written. The risk is purely that a future reader (or a linter, or me) "fixes" one half in isolation and silently destroys the agent — I very nearly did. Would you consider a short comment at both sites noting that the sign convention is paired?

2. mutualinfo disagrees with the rules — the EFE landscape figure plots the wrong surface

In the "standalone predictive / EFE functions" cell:

mutualinfo(Σ) = 1 / 2 * logdet(Σ)

but every :in rule uses MI = -1/2*logdet(Σ), and the notebook's own markdown states the EFE as

$$G(u) = \underbrace{-\tfrac12\log\det\Sigma}_{\text{epistemic}} + \dots$$

mutualinfo is used only by efe_landscape, so the "Expected free energy at step N" heatmap plots a different function than the agent minimises, and its white argmin marker is not the agent's chosen action. Unlike item 1 this one does not cancel — nothing else consumes it.

I verified that flipping it changes only the figure: trajectory and waypoint switches are identical (3/3, [103, 129, 144]). I left it as-is so this PR stays behaviour-preserving — happy to flip it in a follow-up if you agree it is a typo.

3. Latent UndefVarError in one :outprev1 rule

One of the eight :outprev1 rules had arguments named m_inprev1 / m_inprev2 but its body referenced mode(q_inprev1) / mode(q_inprev2). It would have thrown UndefVarError if it ever dispatched. It never does in practice (see below), and PR #94 fixes it incidentally by generating the rule wrappers from their signatures.

4. 34 of the 42 MARX rules never fire

Instrumenting every rule with a dispatch counter, only 8 are ever reached:

rule hits
:out 17250
:in 17160
:inprev1 11010
:outprev1 8790
:inprev2 6660
(conjugate) 4500
:outprev2 2700
(uninformative) 120

The other 34 are dead under this model and constraint set. I kept all of them in PR #94 (they cost nothing at runtime and may be needed for other configurations), but if they were written defensively during development it may be worth pruning them. Your call — you would know which are load-bearing for variants of the model.

5. The planner was under-converging (fixed in #93, but worth a sanity check)

mode(::unBoltzmann) used Fminbox(LBFGS()) with Optim.Options(outer_iterations=100, iterations=1). Comparing it in situ against a properly converged solve on identical inputs, it returned a strictly worse minimum in 5739 of 9270 calls (mean ΔG = −0.80 in favour of the converged solve), and never a better one.

So the published trajectory was shaped partly by the optimiser stopping early. With it converging properly the agent reaches the waypoints noticeably sooner (steps 103/129/144 rather than late in the trial), and all 10 validation seeds still reach 3/3. Could you sanity-check that the new behaviour is what you expect scientifically? It is a real change to the figures, even though it is strictly better EFE minimisation.

🤖 Generated with Claude Code

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Advanced Examples/Autoregressive Active Inference notebook and reproduce the Random.seed!(3), 400-step run using the stated RxInfer and ReactiveMP versions. Inspect the standalone predictive/EFE functions, MARX rules, and mode(::unBoltzmann), including PR #94. Done means the maintainers have resolved or documented the sign and mutualinfo mismatch, checked the latent error and unreachable rules, and validated the optimizer's changed trajectory.

Written by the indexing model from the issue text.

Assessment

Tech stack
julia, jupyter-notebook
Domain
machine-learning
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.