Skip to content

test: search conformance suite layer (i) — metadata golden table for 15 searches #1666

Closed

Description

@Jammy2211

Overview

Phase A0a(i) of the search-extensibility epic (PyAutoMind epics.md; ledger draft/research/autofit/search_extensibility_epic.md, plan §4 A0a, decision D1). A tests-only, parametrised metadata/serialization conformance suite over all 15 public searches that collects and passes on every CI leg, including Heart's unittest-nojax. It freezes a golden identifier table (the epic's §3.2 invariant) so every later refactor phase of track A is checked against it, and it is the only track-A dependency of the autofit_inference harness (B2).

Plan

  • Add one parametrised conformance module over the 15 public searches, resolving each class lazily from a class-path string so the module collects on the no-jax leg.
  • Freeze a golden table per search: default-construction identifier, identifier field tuple, and constructor-argument name set.
  • Assert the search.json round trip and that construction leaves the config untouched, with strict xfails where today's code is known to fail.
  • Prove the table bites: a deliberate perturbation fails exactly one case.
  • Verify on a full-extras env and with the jax family blocked by an import hook, mirroring the unittest-nojax leg.

Tier: judge (undeclared) — merge mode: human /prm

Human ruling 2026-10-07: identifiers must stay the same; any change to an identifier is flagged to the human before it lands.

Detailed implementation plan

Work Classification

Library

Affected Repositories

  • PyAutoFit (primary)

Branch Survey

Repository Current Branch Dirty?
fit/PyAutoFit main (908ca61) clean

Suggested branch: feature/search-conformance-metadata
Worktree root: ~/Code/PyAutoLabs-wt/search-conformance-metadata/

Implementation Steps

  1. test_autofit/non_linear/search/conformance_roster.py: a searches_under_test() helper returning 15 entries (class_path string, short name, family, requires module list). Classes are resolved with importlib inside the test, never at collection. Roster: Emcee, Zeus, BlackJAXNUTS, SMC, DynestyStatic, DynestyDynamic, Nautilus, NSS, Drawer, BFGS, LBFGS, MultiStartAdam, MultiStartADABelief, MultiStartLion, MultiStartProdigy. The module docstring states the roster and the coverage contract.
  2. Skip rule: a construction-dependent case skips only when its requires module is absent (NSS raises ImportError without blackjax ≥ 1.6; every other search constructs with defaults even when its backend is missing). No module-level importorskip.
  3. test_conformance.py, parametrised per search:
    • test_default_construction: cls() succeeds.
    • test_identifier_golden: str(Identifier(search)) with PYAUTO_TEST_MODE removed from the environment (test mode changes Emcee and Zeus identifiers) equals the frozen golden table committed as data in the module. Also freezes each class's __identifier_fields__ tuple.
    • test_constructor_argument_set: the inspect.signature parameter names equal the frozen set.
    • test_search_json_round_trip: af.to_dict → json.dumps/loads → af.from_dict → af.to_dict equal. xfail(strict=True) for BlackJAXNUTS and SMC: the serialised inverse_mass_matrix is a kind string the constructor rejects. Repair goes to A0b and flips the xfail.
    • test_config_unchanged_by_construction: push a config override with output.search_internal: false (the test config has it true and masks the mutation), snapshot the config dict before and after construction, compare. xfail(strict=True) for Emcee, BlackJAXNUTS and SMC until A3.
    • test_family_base: each class subclasses the expected AbstractMCMC / AbstractNest / AbstractMLE.
  4. Generate the golden table once from main, commit it, and verify a deliberate perturbation (one default argument) fails exactly one parametrised case.
  5. Deferred to layer (ii): samples_info key sets (only obtainable from live sampler objects) and any backend execution.

Verification

  • pytest test_autofit/non_linear green on the full-extras env.
  • The same with jax, jaxlib, optax, blackjax blocked via an import hook (mirrors unittest-nojax, where nautilus, zeus, emcee and dynesty remain installed): NSS cases skip, the module still collects.

Key Files

  • test_autofit/non_linear/search/test_conformance.py — new suite
  • test_autofit/non_linear/search/conformance_roster.py — roster helper and golden tables
  • autofit/mapper/identifier.py — identifier hashing read for reference (not edited)
  • autofit/__init__.py — eager exports and lazy NSS/SMC (_LAZY_ATTRS) read for reference (not edited)

Original Prompt

Click to expand starting prompt

Search conformance suite, layer (i): metadata and serialization for all 15 searches on every CI leg (epic search-extensibility, phase A0a(i))

Type: test
Target: autofit
Repos:

  • PyAutoFit
    Themes:
  • searches
  • jax
  • ci
    Difficulty: medium
    Autonomy: safe
    Priority: high
    Epic: search-extensibility
    Status: draft
    Filed: 2026-10-07

Phase A0a(i) of the search-extensibility epic
(draft/research/autofit/search_extensibility_epic.md; plan in
search_extensibility_epic_report.md §4 A0a, decision D1 in §8.1). Tests
only. It is the safety net every later phase of track A runs against and the
only track-A dependency of B2.

Original request (verbatim from the epic plan)

"A0a, conformance suite in two layers. Repo: PyAutoFit, tests only. Layer
(i), metadata/serialization, all 15 searches, every CI leg (including
unittest-nojax; the module never imports af.NSS/af.SMC at collection
time): construction, search.json round trip and constructor-argument sets,
identifiers against a frozen golden table of all 15 default constructions,
declared capability attributes, and conf.instance unchanged by construction
(xfail for Emcee, NUTS and SMC until A3). Risk: none. Verify: pytest test_autofit/non_linear on a full-extras env and with jax/blackjax/optax
uninstalled. Witness: layer (i) green on all legs."

Scope

Add test_autofit/non_linear/search/test_conformance.py (plus a small
searches_under_test() helper module beside it) parametrised over every
public search exported from af:

  • the 15 searches: Emcee, Zeus, NUTS, SMC (mcmc); DynestyStatic, DynestyDynamic,
    Nautilus, NSS (nest); LBFGS, BFGS, Drawer, MultiStartLBFGS/BFGS/Prodigy
    family (mle). Confirm the exact roster from autofit/non_linear/search/
    and the af exports before freezing the table, and record it in the module
    docstring.
  • Collection never imports an optional backend: resolve search classes lazily
    by class_path string inside the test (importlib) so the module collects on
    the unittest-nojax leg; a search whose backend is absent is skipped by
    that fact alone for the construction-dependent checks
    , never by a
    module-level importorskip. Emulate the no-jax leg locally with an
    import-hook block before shipping (memory: nojaxSkip).
  • Per search: default construction; search.json write → from_dict
    round trip equals the original (compare the serialised dicts); the
    constructor-argument set snapshot; identifier of the default construction
    against a frozen golden table committed as data in the test module (this
    table is the §3.2 invariant every later phase of the epic must hold);
    declared capability attributes present where they already exist today
    (assert only what exists; A1 adds the full set); conf.instance unchanged
    by construction, with pytest.mark.xfail(strict=True) for Emcee, NUTS and
    SMC until A3.
  • Snapshot the per-search samples_info key sets only if obtainable without
    running a backend; otherwise defer to layer (ii).

Out of scope

Backend execution (layer (ii), after A0b), any source edit under autofit/,
the hygiene list (A0b) and the documentation repairs (A0c).

Verification

  • pytest test_autofit/non_linear green in a full-extras env.
  • The same green with jax/blackjax/optax blocked via an import hook (the
    unittest-nojax leg); JAX-only searches skip, the module still collects.
  • Golden table: regenerate once, commit, and prove a deliberate identifier
    perturbation fails the test.

Witness

Layer (i) green on every CI leg; deleting a row from the golden table or
changing a default argument fails exactly one parametrised case.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QJmrnXNQdq6HSruUt3MqVW

Activity

  1. Jammy2211 commented on Oct 7, 2026

    @Jammy2211
    CollaboratorAuthor

    Library PR Created

    API changes: none, tests only (test_autofit/non_linear/search/{conformance_roster,test_conformance}.py).

    Local evidence (task worktree, branch head 886ebb2): full test_autofit 3066 passed / 2 skipped / 5 xfailed; conformance module 101 passed / 5 strict xfails; no-jax leg emulated via import hook (97 passed, 4 NSS skips, 5 xfails); perturbation of one golden identifier fails exactly one case.

    Strict xfails recorded for later epic phases: NUTS/SMC search.json round trip (A0b), Emcee/NUTS/SMC output.search_internal mutation at construction (A3). No identifiers changed.

    Workspace impact: none (option iii). Heart YELLOW acknowledged by the human at ship (manifest drift from the unregistered autofit_inference/autofit_profiling checkouts, B1 pending; no release rehearsal for current source).

    Next: human /prm once CI is green (judge tier).

  2. Jammy2211 commented on Oct 7, 2026

    @Jammy2211
    CollaboratorAuthor

    Shipped

    Summary: layer (i) search conformance suite — 15 public searches, frozen golden identifier table from main 908ca61, search.json round trip, constructor-argument sets, conf.instance unchanged by construction; collects and passes on unittest-nojax. Five strict xfails record the A0b (NUTS/SMC round trip) and A3 (Emcee/NUTS/SMC output.search_internal mutation) repairs. No identifiers changed.

    Session notes: the merge waited on a Heart release-validation freeze (expired 20:36Z). Epic search-extensibility phase A0a(i) complete; B1 registration is in flight (PyAutoMind#492).

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

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