Provenance and determinism#
Closed beta (nothing installed): the receipt this page describes rides every suggestion the hosted service answers (Hosted beta: getting started pastes one); the replay example runs the engine in your process and is for readers with repository access.
The receipt#
The receipt. Its identity, the seven fields through the seed, fixes the suggestion together with the recorded state and the ask’s intents: the installed engine resolves the triple, the config’s canonical form hashes to config_hash, and the seed is the config’s explicit value or a hash of the strategy, its params, and the state scope (state_hash, or space_hash for sobol). Everything past the seed enters no hash or seed: fallback_from names the descent, the twelve records from timings down are audit, and on the hosted plane the service appends three transport keys of its own (queue_wait_s, lane, attempt) beside them. A replay rebuilds the recorded state as it stood at the ask (the observation history and the open suggestions, in canonical form) in a fresh process, asks with the pinned config and the ask’s own intents, and on an engine whose triple matches – with no wall budget binding, or in caps mode – gets the same assignments with the receipt’s strategy, config_hash, and seed.#
Every suggestion carries a receipt with enough identity to re-derive it. Its fields:
The identity triple:
engine_version(the release tag),engine_sha(the engine build), anddeps_fingerprint(the numerics-relevant dependency set). How each one is resolved is the next section.strategy, the rung that answered;policy, the router that chose it (Nonewhen the config pins a strategy other thanauto);config_hash, the content address of the config;seed, the integer the strategy ran with.fallback_from, the intended strategy when a ladder descent answered, andfallback_attempts, one record per failed rung with its verbatim error (the local driver; the hosted plane serves these records, andconditioning_events, without theirexception_text– the text stays in the service’s stored provenance, the operator’s database, never the wire).timings, the wall-clock split between Gaussian-process (GP) fitting and acquisition optimization, accumulated across every rung attempted; absent when no attempted rung recorded a fit or acquisition phase (a space-filling answer that followed a failed GP rung still carries the GP rung’s timings).dedup, the deduplication record: collisions, repair counts, per-draft origins, the exhaustion proof, intent refusals, and the uniqueness status.GP internals when a fit ran:
fits(each fitted model’s parameters as dtype, shape, and a digest of the raw bytes),work(the effective optimizer knobs),acq_events,aggregation(duplicate-point collapsing),conditioning_events(noise-floor retries), andbase_noise.Three more audit records when their mechanisms ran:
center(the branchcenter_in_bootstraptook),selection(one queue-selection decision per slot), andacq_init_filters(the armed finite-filtering initializer’s per-attempt receipt).
Everything past the seed is audit and enters no hash or seed, so a receipt can gain fields across versions without moving an older one.
The receipt is also the observability surface. The library emits no log
records (no module in tauopt creates a logger), so a quiet run is a normal
run and a descent shows up in fallback_from, never on a console. The only
messages tauopt itself can write to stderr are two Python warnings
warnings from the armed acquisition initializer on gp_nehvi’s retry path:
one when no raw candidate scores a non-zero acquisition value and the initial
conditions fall back to random draws, one on a Sobol dimension overflow; the
ask still answers. Warnings from the tensor libraries pass
through unfiltered.
How the identity triple is resolved#
engine_version is the installed distribution’s version. engine_sha
resolves in a fixed order: the TAUOPT_ENGINE_SHA environment variable when
it is set to a non-empty value; else the tauopt._build_info module that
python/hatch_build.py stamps into every wheel and sdist at build time (the
commit the distribution was built from, so a plain pip install of a built
wheel carries the real sha); else git rev-parse HEAD when the installed
provenance.py sits inside a checkout of this repository at its source
path (a source checkout or an editable install pip install -e "./python[all]", which carries no stamped module); else the literal string
unknown. The git leg checks the checkout’s layout, so a wheel installed
into a venv that lives under some other git repository reads its own stamp
and never that repository’s HEAD. unknown therefore means a build in
which no sha source was available (no TAUOPT_ENGINE_SHA, no git metadata,
no prior stamp: a source tree copied without its .git, or an older build)
installed outside any checkout; TAUOPT_ENGINE_SHA=$(git rev-parse HEAD)
exported before the run overrides every leg. Two receipts that both read
unknown agree on the sha without saying anything about the build.
deps_fingerprint hashes the name==version lines of nine distributions,
in this order: tauopt, numpy, scipy, torch, botorch, gpytorch,
linear_operator, pydantic, and httpx (a missing one hashes as
absent). The hash is one-way, so store the lines themselves next to any
receipt you intend to replay. The engine exposes the list; the snippet
prints the set behind every fingerprint on this page, then shows the
override winning:
import os
from importlib import metadata
from tauopt.engine.provenance import _FINGERPRINT_DISTS, deps_fingerprint, engine_sha
for dist in _FINGERPRINT_DISTS: # the engine's own list
try:
print(f"{dist}=={metadata.version(dist)}")
except metadata.PackageNotFoundError:
print(f"{dist}==absent")
print(deps_fingerprint())
print(engine_sha() == "unknown") # False in a checkout or an editable install
os.environ["TAUOPT_ENGINE_SHA"] = "build-1234"
print(engine_sha()) # the declared value wins
tauopt==0.4.0
numpy==2.4.6
scipy==1.18.0
torch==2.12.1+cpu
botorch==0.18.1
gpytorch==1.15.2
linear_operator==0.6.1
pydantic==2.13.4
httpx==0.28.1
env_blake2b_ad8a0b2c3ff0d12d
False
build-1234
TAUOPT_DEPS_FINGERPRINT overrides the fingerprint the same way
TAUOPT_ENGINE_SHA overrides the sha (a non-empty value wins; an empty one is
ignored). It exists for service builds, which
stamp a hash of their lock file into the image, so a hosted receipt’s
fingerprint is a build stamp rather than a hash of installed versions: it
never equals a fingerprint computed on your machine, however closely the
versions match. Replaying a hosted suggestion therefore means re-asking the
service with the same recorded state and config; a local replay of a hosted
receipt would need that build’s dependency set installed, which the receipt
does not carry.
What cross-process determinism rests on#
The contract is that engine version, engine build, dependency fingerprint, config hash, state, and seed together fix the suggestion. Three mechanisms make it hold.
Seeds never come from process state. An explicit seed wins verbatim.
Otherwise the seed derives, through a BLAKE2b hash, from the strategy, its
params, and a scope hash: the space hash for sobol, so its scramble stays
fixed and the sequence continues across asks, and the full state hash for
the point strategies, so successive asks differ. Top-level config flags
never feed the seed, so turning dedup or a timebox mode on cannot move the
underlying stream.
The GP rungs pin their compute: single-threaded, double precision, under a
forked random-number stream seeded from the resolved seed, so load on the
machine cannot change a fit. And the fingerprint names the dependency set
because minor bumps of the tensor libraries move numerics; a matching
version and sha with a different fingerprint may be a different computation
(two of the nine distributions, pydantic and httpx, are wire-layer
packages whose bumps move the fingerprint without moving any number).
import json
from tauopt import ExperimentState, OptimizerConfig, Parameter, Space, ask
from tauopt.engine import Objective, ObservationRecord
space = Space(parameters=(
Parameter(name="x", type="double", bounds=(0.0, 1.0)),
Parameter(name="k", type="integer", bounds=(1, 5)),
))
state = ExperimentState(space=space, objective=Objective(metric="score", goal="max"))
for i, k in enumerate((1, 3, 5, 2, 4)):
state = state.with_observation(ObservationRecord(assignments={"x": i / 5, "k": k}, value=float(i % 3)))
config = OptimizerConfig(strategy="gp_ei")
result = ask(state, config=config)
p = result.provenance
print(json.dumps({
"drafts": [d.assignments for d in result.drafts],
"strategy": p.strategy, "seed": p.seed, "config_hash": p.config_hash,
"state_hash": state.state_hash(), "deps": p.deps_fingerprint,
}, sort_keys=True))
{"config_hash": "blake2b_9d65974ae3d0347b", "deps": "env_blake2b_ad8a0b2c3ff0d12d", "drafts": [{"k": 5, "x": 0.0}], "seed": 1021186392863887800, "state_hash": "blake2b_8517303f875f0e7e", "strategy": "gp_ei"}
A second, separate process printed the same line byte for byte. The premise
is the same engine and the same dependency set. The fitted-parameter digests
in fits cover the case where that premise fails quietly: two replicas with
different fitted state disagree on the receipt instead of agreeing by
accident. The benchmark suite observes this over every cell of a campaign:
The benchmark suite.
Timebox modes and purity#
The config field timebox_mode sets how a GP rung relates to the clock.
budget(the default, also what an omitted field means at execution) gives each GP rung a cooperative wall-clock budget. An overrun descends the ladder, so which rung answers can depend on machine load. The receipt records the executed rung and seed; what is not reproducible under a binding budget is the choice of rung and, when the fit itself is cut short, the fitted hyperparameters of the executed rung; a non-binding timebox,off, orcapsre-derive exactly.offinstalls no timeout machinery (when no ask-levelmax_wait_sis set; a request deadline re-arms a wall budget on the executed rung); with no request deadline, fits and optimizations run to convergence, and no clock read can influence the answer.capskeepsoff’s clock-free structure and adds deterministic work ceilings (fit_maxiter,acq_maxiter). It is accepted at the top level only, because a params-form setting would not survive a ladder descent.
off and caps are the pure modes: the answer is independent of wall time.
An ask-level max_wait_s bounds the executed rung under budget and off.
Under caps it never shapes the computation: the ask refuses at a rung
boundary or at the single delivery decision after the work completes, with
AskDeadlineExceeded, and the computed drafts (candidate points) are
discarded rather than substituted. Availability may depend on the clock; the
answer never does. The explicit budget value hashes differently from the
omitted default even though it executes the same way:
from tauopt import OptimizerConfig
base = OptimizerConfig(strategy="gp_ei")
print(base.canonical_json(), base.config_hash())
for mode in ("budget", "off", "caps"):
cfg = OptimizerConfig(strategy="gp_ei", timebox_mode=mode)
print(mode, cfg.config_hash(), cfg.config_hash() == base.config_hash())
seeded = OptimizerConfig(strategy="gp_ei", seed=7)
print(seeded.canonical_json(), seeded.config_hash())
{"params":{},"seed":null,"strategy":"gp_ei"} blake2b_9d65974ae3d0347b
budget blake2b_218b394836e2ee5f False
off blake2b_5941d0f3ef8511cc False
caps blake2b_85436d8936578d44 False
{"params":{},"seed":7,"strategy":"gp_ei"} blake2b_2e698b2aceb47132
The canonical form behind config_hash keeps three fields always:
strategy, params (as {} when empty), and seed (as null when
unset), as the first line shows. The seven later fields drop out at their
defaults, center_in_bootstrap at false, dedup at true,
duplicate_intents, queue_selection, categorical_mode, and
timebox_mode at null, and base_noise at 1e-3, and serialize at any
other value, which is why the explicit budget hashes differently from the
omitted field.
What a replay needs#
The receipt identifies the run; it does not carry the state. A replay needs
the recorded experiment state as it stood at the ask (observation history
plus open suggestions, in canonical form), the config behind config_hash,
the seed, the ask’s own intents when it carried any, and an engine whose
version, sha, and fingerprint match (two unknown shas match without proving
anything; see the resolution above).
With a descended rung, replay the rung named in strategy rather than the
one in fallback_from.
Do it: Replay a suggestion.