Ordered search-space contract

OrderedSearchSpaceSpec is the portable, fingerprinted description of a tuning space shared by DAG-ML hosts and the native n4m optimizer. It is a vector of axes: array order, categorical-choice order, activation and the structured ParameterPatch target are all semantic.

The normative JSON shape is docs/contracts/ordered_search_space.v1.schema.json. The first golden contract is parity/hpo/contracts/ordered_search_space.v1.json.

Ownership boundary

  • A binding or DAG host owns the mapping from an axis to its structured patch.

  • libn4m receives the ordered axis definitions and returns trial values in that same order.

  • DAG-ML evaluates the patched pipeline and reports the score through ask/tell.

  • DAG-ML does not link to libn4m, and n4m does not execute a host DAG.

Canonical values

Raw JSON numbers are excluded from fingerprinted parameter domains:

  • signed integers use canonical decimal {"i64":"-12"};

  • floats use their IEEE-754 binary64 bits as 16 lowercase hexadecimal digits, for example 1.0 is {"f64_bits":"3ff0000000000000"};

  • -0.0 is normalized to positive zero;

  • NaN and infinities are invalid;

  • strings must be non-empty, NFC-normalized UTF-8 (the empty label is reserved by the current C ABI as the no-label sentinel);

  • booleans remain typed booleans.

This prevents host number parsers and locale formatting from changing a study fingerprint. V1 restricts every integer domain bound, step, tuple bound, and integer choice to [-2^53, 2^53], because the current native representation must marshal it through binary64 without silent rounding. Choice domains are homogeneous: one categorical or ordinal axis cannot mix scalar kinds. Integer steps are positive, log-integer steps are exactly one, and log-float domains are continuous (step = 0).

Activation and constraints

V1 activation is always, when, or when_not, with a parent choice index. Parents must be categorical or ordinal, all parents must exist, and the activation graph must be acyclic. Inactive axes use sample_then_omit, matching the current native sampler trajectory while preventing inactive values from becoming ParameterPatch entries.

Hard constraints are mutex_group, requires, and exclude. A constraint reference can address axis presence or one categorical/ordinal choice index. Reference identity is the pair (axis, choice_index-or-bare): two distinct choices of one axis are distinct atoms, while an exact repeated pair is invalid. Support still depends on the selected sampler: unsupported sampler × constraint combinations fail before the first trial; they never degrade to best-effort sampling.

Current C ABI projection

The current builder API is a projection of this host contract, not an alternate wire format:

  • axis array order and names become n4m_search_space_add_* call order;

  • numeric and choice domains map to the matching builder, subject to the exact binary64 restrictions above;

  • when and when_not become CONDITION_IN and CONDITION_NOT_IN records;

  • a choice index is resolved to the native choice label before adding a condition or hard constraint: the label is the string itself, canonical decimal integer, true/false, or the locale-independent binary64 round-trip text used by the C++ builder;

  • target and inactive_policy remain host coordination metadata and are not discarded from the full contract fingerprint.

The projected native SearchSpace therefore cannot reproduce the full fingerprint by itself. The normative fingerprint belongs to this complete ordered contract; DAG-ML stores it as an opaque study input and must not recompute it with DAG-ML’s unrelated internal JSON fingerprint helper. A future native JSON entry point may validate and fingerprint the complete contract, but must match the published vectors byte for byte.

Fingerprint

The fingerprint is sha256: followed by SHA-256 of the RFC 8785/JCS canonical UTF-8 representation. Object properties are ordered by UTF-16 code units; arrays are never reordered. Because all domain numbers are tagged strings, the only numeric JSON tokens in V1 are non-negative structural integers such as schema_version, tuple length, and choice indices. This restricted value domain avoids host-dependent floating-point JSON rendering. Structural integers stay within the interoperable JSON safe-integer range; tuple length is additionally bounded by the current C ABI’s signed 32-bit argument.

Display names, documentation and UI metadata are deliberately outside the fingerprinted object. Changing axis order, choice order, target, activation or constraint always changes the fingerprint.

The test-only oracle in parity/hpo/ordered_space_contract.py validates the V1 invariants and pins the golden fingerprint. Production bindings must use the eventual native validator/fingerprint API; they must not fork this Python oracle.