ReactiveBayes / ReactiveBayes/RxInferExamples.jl
Autoregressive Active Inference: sign conventions, unreachable rules, and an EFE-landscape figure mismatch
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)usesQ(y) = -logpdf(left, y) - right.G(y), whereas the analogousprod(::ClosedProd, ::AbstractMvNormal, ::unBoltzmann)uses+ right.G(u). Sincelogpdf(::unBoltzmann) = -G, the product's negative log density "should" be-logpdf(left) + right.G.- The
:outprev1rules returnG(outprev1) = logpdf(marx_predictive(...), m_star)— a positive log density as an energy, where-logpdfwould 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
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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