Skip to content

feat: cross-Analysis shared per-evaluation state in FactorGraphModel#1308

Open
Jammy2211 wants to merge 1 commit into
mainfrom
feature/analysis-shared-state
Open

feat: cross-Analysis shared per-evaluation state in FactorGraphModel#1308
Jammy2211 wants to merge 1 commit into
mainfrom
feature/analysis-shared-state

Conversation

@Jammy2211
Copy link
Copy Markdown
Collaborator

Summary

Adds an opt-in mechanism for the per-factor Analysis objects of a FactorGraphModel to compute a model-dependent object once per likelihood evaluation and reuse it across every factor, instead of each factor recomputing identical work. This is the generic, domain-agnostic foundation for the ALMA datacube likelihood (sub-task B, tracked separately), where ~97% of each channel's inversion-setup work is identical across channels and currently rebuilt N times.

The design mirrors the existing EPAnalysisFactor precedent (graph-level state attached around the factor loop) and Analysis.modify_before_fit (the precompute-then-reuse hook), but runs once per likelihood evaluation rather than once per fit. It is fully backward compatible: when no factor opts in, every graph behaves exactly as before.

Resolves part of #1307 (autofit deliverable; the 1D Gaussian workspace tutorial and fast tests follow).

API Changes

All changes are additive and backward compatible — no removals or renames.

  • Added Analysis.shared_state_from(instance) -> None: an opt-in hook (default returns None) that computes a per-evaluation object shared across a FactorGraphModel's factors. The per-evaluation, cross-factor sibling of modify_before_fit.
  • Analysis.log_likelihood_function (and the AnalysisFactor / PriorFactor / HierarchicalFactor variants) gain an optional shared=None keyword argument. Existing analyses that do not declare it keep working because FactorGraphModel forwards shared only when a factor opts in.
  • af.ex.Analysis gains an opt-in share_model_data=False constructor flag plus a worked shared_state_from implementation, demonstrating the mechanism on the 1D Gaussian toy.

See full details below.

Test Plan

  • python -m pytest test_autofit/graphical/ — 188 existing + 4 new tests pass
  • New test_autofit/graphical/test_shared_state.py:
    • shared state computed once per evaluation (not once per factor)
    • shared-path likelihood exactly equals the unshared per-factor sum
    • a graph with no opt-in factor is unchanged
    • the default shared_state_from returns None

(Note: the full test_autofit/ suite has 13 pre-existing failures under test_autofit/non_linear/search/nest/nss/ that are unrelated to this change and already broken on main.)

Full API Changes (for automation & release notes)

Added

  • autofit.non_linear.analysis.analysis.Analysis.shared_state_from(self, instance) — returns None by default; override to share per-evaluation state across FactorGraphModel factors.
  • autofit.example.analysis.Analysis.__init__(..., share_model_data=False) — opt-in flag enabling the shared-state path on the 1D Gaussian example.
  • autofit.example.analysis.Analysis.shared_state_from(self, instance) — worked example: computes the shared model data once when share_model_data=True.

Changed Signature (backward compatible — new optional kwarg with default)

  • Analysis.log_likelihood_function(self, instance)(self, instance, shared=None)
  • AnalysisFactor.log_likelihood_function(self, instance)(self, instance, shared=None)
  • PriorFactor.log_likelihood_function(self, instance)(self, instance, shared=None) (accepts and ignores shared)
  • HierarchicalFactor family log_likelihood_function(self, instance)(self, instance, shared=None) (accepts and ignores shared)
  • autofit.example.analysis.Analysis.log_likelihood_function(self, instance, xp=np)(self, instance, shared=None, xp=np)

Changed Behaviour

  • FactorGraphModel.log_likelihood_function now asks the lead factor for a shared object (_shared_state_from) before the per-factor loop and forwards it to each factor when non-None. When no factor opts in (the default), behaviour is unchanged.

Migration

  • None required. Existing Analysis subclasses and graphs work unchanged. To opt in, override shared_state_from to return a shared object and have log_likelihood_function(self, instance, shared=None) consume it (falling back to its own computation when shared is None).

🤖 Generated with Claude Code

Add an opt-in mechanism for the per-factor Analysis objects of a
FactorGraphModel to compute a model-dependent object once per likelihood
evaluation and reuse it across every factor, instead of each factor
recomputing identical work.

- Analysis.shared_state_from(instance) -> None: opt-in hook (default None),
  the per-evaluation cross-factor sibling of modify_before_fit.
- Analysis.log_likelihood_function gains an optional shared= kwarg.
- FactorGraphModel computes the shared object once from the lead factor and
  forwards it to each factor only when non-None, so existing graphs are
  byte-for-byte unchanged.
- AnalysisFactor forwards shared=; PriorFactor and HierarchicalFactor accept
  and ignore it for a uniform calling convention.
- af.ex.Analysis demonstrates it on the 1D Gaussian toy via an opt-in
  share_model_data flag.
- New unit tests in test_autofit/graphical/test_shared_state.py.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Jammy2211
Copy link
Copy Markdown
Collaborator Author

Workspace PR: PyAutoLabs/autofit_workspace#69

@Jammy2211
Copy link
Copy Markdown
Collaborator Author

Workspace PR: PyAutoLabs/autofit_workspace_test#31

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pending-release PR queued for the next release build

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant