Skip to content

refactor: defer heavy imports — import autolens 4.1s → ~1.9s #1505

Description

@Jammy2211

Overview

import autolens costs ~4.1 s (full example-script stanza ~4.4 s), and profiling shows ~2.5 s of it is heavy packages loaded eagerly but not needed at import: the jax chain (nufftax + blackjax + optax, 1.75 s — 43%, measured by control test), IPython, sqlalchemy, numba (decoration-time), and astropy. All 457 autolens_workspace scripts pay this per-process (~30 min of pure import per full serial smoke sweep), and smoke runs with JAX disabled, so its jax import there is pure waste. This task defers each of these to first use across PyAutoFit, PyAutoArray and PyAutoNerves, targeting import autolens ≤ ~1.9 s with zero public-API change.

Plan

  • Defer the jax chain: lazy af.NSS (PEP 562) in PyAutoFit; lazy nufftax load + batcher patch in PyAutoArray's TransformerNUFFT.
  • Defer IPython, sqlalchemy (lazy sa proxy + annotation fixes), and the eager autofit.database pulls in PyAutoFit.
  • Defer numba to first call via a lazy-compile wrapper in autoarray.numba_util.jit (fixes all 29 decorated functions centrally).
  • Defer astropy in autonerves.fitsable; dedupe the workspace-version warning (currently fires 3× per process).
  • Verify: import-time absence proof, full suites in all three repos, NSS / NUFFT / aggregator behaviour runs, targeted workspace smoke incl. # ENV: jax scripts.
  • Scoped out: matplotlib deferral (enters via the autolens/autogalaxy visualizer chain; every workspace script imports aplt anyway, so no smoke/user win — optional follow-up for bare-library import only).
Detailed implementation plan

Affected Repositories

  • PyAutoFit (primary)
  • PyAutoArray
  • PyAutoNerves

Branch Survey

Repository Current Branch Dirty?
./PyAutoFit main clean
./PyAutoArray main clean
./PyAutoNerves main clean

Suggested branch: feature/lazy-heavy-imports

Note: PyAutoFit is also claimed by stored-sample-reconstruction-guard (#1486, library half shipped as PR#1504) — deliberate file-disjoint override, recorded in Mind active.md. The version-stamp-sync-guards branch also touches autofit/__init__.py (version-stamp lines); coordinate at merge if both are in flight.

Implementation Steps (PyAutoFit, ~0.9 s)

  1. autofit/__init__.py — remove eager imports of NSS (L98), GridSearchAggregator (L15), Aggregator (L37), Query (L42); add PEP 562 module __getattr__ resolving these four lazily, caching in globals() (pattern: autolens/__init__.py:165-181). NSS stays fully supported — lazy, never removed.
  2. autofit/non_linear/fitness.py — move from IPython.display import clear_output (L3) into the single use site (~L439, quick-update branch).
  3. autofit/database/sqlalchemy_.py — make sa/declarative lazy proxies: __getattr__ imports sqlalchemy on first attribute access and caches; fall back to the existing MockSQlAlchemy on ImportError.
  4. Add from __future__ import annotations to modules evaluating sa.orm.Session in signatures at def time (Py3.12): non_linear/settings.py, search/mcmc/abstract_mcmc.py, search/nest/abstract_nest.py, search/nest/nss/search.py, search/nest/nautilus/search.py, plus a full per-file sweep of every sqlalchemy_ importer. Verify no eager __annotations__ introspection in these modules.
  5. autofit/non_linear/paths/database.py — defer Fit and Info imports to function-local at their use sites (~L112, L236-243, L345).
  6. autofit/non_linear/grid/grid_search/result_builder.pyfrom __future__ import annotations + move from autofit.database import Prior (annotation-only) under if TYPE_CHECKING.

Implementation Steps (PyAutoArray, ~0.75 s)

  1. autoarray/operators/transformer.py — replace module-level nufftax try-import (L26-29) and version-gated _patch_nufftax_batchers() call (L90-94) with an idempotent _load_nufftax() (import, 0.6.x check, batcher patch — a global JAX registration that must precede the first traced call) invoked at the top of TransformerNUFFT.__init__ (existing _nufftax is None guard at ~L646). All other _nufftax uses are in TransformerNUFFT methods.
  2. autoarray/numba_util.pyjit() returns a wrapper that on first call imports numba (falling back to the plain function on ModuleNotFoundError), compiles with the same nopython/cache/parallel/fastmath options, caches, delegates. Sweep for attribute access on decorated functions (.py_func etc.) first.

Implementation Steps (PyAutoNerves, ~0.17 s + noise)

  1. autonerves/fitsable.py — remove module-level astropy try-import (L12-15, keep TYPE_CHECKING block); lazy _fits() accessor used at the six runtime sites (~L63, 86, 88, 90, 210, 238).
  2. autonerves/workspace.py — module-level _warned_messages set; the three warnings.warn sites (~L227, 237, 257) skip byte-identical repeats, so import autolens warns once, not three times. The direct-call tests use distinct tmp_path roots — unaffected.

Key Files

  • PyAutoFit/autofit/__init__.py — lazy NSS/Aggregator/Query/GridSearchAggregator
  • PyAutoFit/autofit/database/sqlalchemy_.py — lazy sa proxy
  • PyAutoArray/autoarray/operators/transformer.py — lazy nufftax + batcher patch
  • PyAutoArray/autoarray/numba_util.py — lazy-compile jit wrapper
  • PyAutoNerves/autonerves/fitsable.py, PyAutoNerves/autonerves/workspace.py

Verification

  • Timing: 3× cold import autolens before/after; target ≤ ~1.9 s (matplotlib retained by design).
  • Absence proof: python -X importtime -c "import autolens" shows no jax/jaxlib/blackjax/optax/nufftax/IPython/sqlalchemy/numba/astropy.
  • Behaviour: af.NSS/af.Aggregator/af.Query/af.GridSearchAggregator resolve; run autofit_workspace_test/scripts/searches/NSS.py, an interferometer TransformerNUFFT script, a database/aggregator flow.
  • Full pytest suites in PyAutoFit, PyAutoArray, PyAutoNerves; downstream autogalaxy/autolens suites via ship_library; unittest-nojax leg stays green.
  • Targeted workspace smoke incl. # ENV: jax scripts before PRs open.

Follow-up (not this task)

  • Brain RefactorDecision flags pyautonerves as "unwitnessed" — its witness map lacks test_autonerves; fix the map.
  • Optional: matplotlib deferral for bare-library import (unit-test startup), via the autolens/autogalaxy visualizer chain.

Original Prompt

Click to expand starting prompt

Can you do a profiling of how long import times on autolens_workspace
example scripts and performance in general is, and assess if we can speed
it up as it is important for running smoke tests and general user
performance being fast

Full measured profile in PyAutoMind/active/import_time_lazy_heavy_imports.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions