Skip to content

fix(inversion): make AbstractMeshGeometry picklable (xp module → _use_jax bool + property) #320

@Jammy2211

Description

@Jammy2211

Overview

Carve-out from PyAutoFit #1279 (Phase 4 of the JAX visualization roadmap). A picklability spike found that FitImaging cannot be pickled today because AbstractMeshGeometry.__init__ stores self._xp = xp — the literal numpy or jax.numpy module — and Python can't pickle modules. This blocks sending a FitImaging over an mp.Process+Queue or ProcessPoolExecutor boundary, which is the production design for Phase 4 subprocess visualization.

Fix: replace the module-attribute pattern with the _use_jax: bool + _xp property pattern already used in Analysis._xp (PyAutoFit) and AbstractMaker._xp (PyAutoArray decorators per CLAUDE.md). Eliminates the pickle barrier at the API level instead of working around it with __getstate__/__setstate__ hooks.

Spike evidence (PyAutoFit #1279):

  • Before fix: TypeError: cannot pickle 'module' object at inversion.linear_obj_list[0].interpolator.mesh_geometry._xp
  • After fix (proven via monkey-patch): FitImaging round-trips through pickle.dumps/loads with log_likelihood delta 0.00e+00 on both numpy and JAX backends. Pickle size ~4.6 MB.

Plan

  • Change AbstractMeshGeometry.__init__ to store self._use_jax = xp is not np instead of self._xp = xp
  • Add _xp as a @property deriving from self._use_jax (returns np or jnp on demand)
  • All existing call sites read self._xp (no writes) — they continue to work transparently via the property
  • Add a unit test that pickles a MeshGeometryRectangular instance and verifies _xp round-trips to the correct module
Detailed implementation plan

Affected Repositories

  • PyAutoArray (primary, library, only repo touched)

Work Classification

Library

Branch Survey

Repository Current Branch Dirty?
./PyAutoArray main clean (canonical) — knn-barycentric task holds it via worktree, file-disjoint

Suggested branch: feature/mesh-geometry-picklable
Worktree root: ~/Code/PyAutoLabs-wt/mesh-geometry-picklable/

Conflict resolution: File-level coexistence on PyAutoArray with knn-barycentric (#317). KNN edits inversion/mesh/interpolator/, inversion/mesh/mesh/, and plot/* — no overlap with our scope (inversion/mesh/mesh_geometry/abstract.py). Same precedent as ag-interferometer-kwargs.

Implementation Steps

  1. autoarray/inversion/mesh/mesh_geometry/abstract.py:
    • In __init__: replace self._xp = xp with self._use_jax = xp is not np
    • Add @property method _xp that returns jax.numpy if _use_jax else numpy
    • Import of jax.numpy stays lazy (only when _use_jax=True and _xp is accessed) — matches Analysis._xp pattern in PyAutoFit
  2. test_autoarray/inversion/pixelization/mesh_geometry/test_picklability.py (new):
    • test_picklable_numpy_backend — construct via __new__ + _use_jax=False, pickle round-trip, assert restored._xp is np
    • test_picklable_jax_backend — same with _use_jax=True, assert restored._xp is jnp (pytest.importorskip('jax'))
  3. Run the existing mesh_geometry tests + the new pickle tests + any pixelization tests that touch _xp to verify no regression

Key Files

  • PyAutoArray/autoarray/inversion/mesh/mesh_geometry/abstract.py (line 23 + new property)
  • PyAutoArray/test_autoarray/inversion/pixelization/mesh_geometry/test_picklability.py (new)

Out of scope

  • __getstate__/__setstate__ workaround (Option A in the parent issue's spike) — rejected in favour of the cleaner property pattern (Option B)
  • Subprocess visualization itself — that's the parent task #1279
  • FitInterferometer NUFFT picklability — separate spike under #1279

Parent

PyAutoFit #1279 — Phase 4 subprocess visualization feasibility (carve-out of the picklability prereq)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions