85def recorded_space(study: StudySpec) -> dict[str, Any]:
86 """Return the version-independent declarative space recorded in results."""
88 for name, parameter
in study.search_space.items():
89 value: dict[str, Any] = {
"kind": parameter.kind}
90 if parameter.kind
in {
"integer",
"real"}:
97 elif parameter.kind ==
"categorical":
98 value[
"choices"] = list(parameter.choices)
99 parameters[name] = value
100 if study.objective.kind !=
"expression":
101 raise ValueError(
"space identity requires an expression objective")
103 "parameters": parameters,
105 "expression": study.objective.expression,
106 "direction": study.objective.direction,
107 "constraints": list(study.objective.constraints),
390 data_context: ContinuationDataContext |
None =
None,
392 """Auto-detect fd-3 JSONL, a complete result object, or its trials array."""
394 source = Path(path).expanduser().resolve()
395 content = source.read_bytes()
396 if len(content) > 256 * 1024 * 1024:
397 raise ValueError(
"warm-start document exceeds 256 MiB")
398 text = content.decode(
"utf-8")
400 document = _loads(text)
401 except json.JSONDecodeError:
404 if isinstance(document, dict)
and "trials" in document:
405 _validate_data_context(
406 document, data_context, required=
True, complete=data_context
is not None
408 if "tpe_sampler_state" in document:
409 checkpoint = document[
"tpe_sampler_state"]
410 _validate_sampler_checkpoint(checkpoint)
411 if document.get(
"trials_out",
"all") !=
"all":
412 raise ValueError(
"summary/none result is not a complete trial history")
413 own_trials = document[
"trials"]
414 inherited = document.get(
"warm_start_trials", [])
415 if "trials_completed" in document
and (
416 type(document[
"trials_completed"])
is not int
417 or document[
"trials_completed"] != len(own_trials)
419 raise ValueError(
"result does not contain all completed trials")
420 if "warm_start" in document
and (
421 type(document[
"warm_start"][
"trials"])
is not int
422 or document[
"warm_start"][
"trials"] != len(inherited)
424 raise ValueError(
"result is missing ancestor trials")
425 trials = inherited + own_trials
426 parent_space = document.get(
"space")
427 if parent_space
is None and "study_spec" in document:
428 embedded = document[
"study_spec"]
430 parameters = _parse_search_space(
431 embedded[
"strategies"][0][
"search_space"],
432 "$.strategies[0].search_space",
435 objective = _parse_objective(embedded[
"objective"], issues)
437 raise ValueError(
"invalid legacy study_spec space or objective")
438 parent_space = recorded_space(
441 strategy=replace(study.strategy, search_space=parameters),
445 if parent_space
is None and "study" in document:
446 parent_spec = Path(document[
"study"])
447 if not parent_spec.is_absolute():
448 parent_spec = source.parent / parent_spec
449 parent_space = recorded_space(
450 load_study_spec(parent_spec, continuation=
True)
452 elif isinstance(document, list):
455 trials = [_loads(line)
for line
in text.splitlines()
if line.strip()]
456 if not isinstance(trials, list)
or not trials:
457 raise ValueError(
"parent has no trials")
458 expected = canonical_space(recorded_space(study))
459 expected_hash = hashlib.sha256(expected.encode()).hexdigest()
461 def verify_hash(owner):
462 version = owner.get(
"space_hash_version")
463 if version
is not None and type(version)
is not int:
464 raise ValueError(
"space_hash_version must be an integer")
466 version == SPACE_HASH_VERSION
467 and "space_hash" in owner
468 and owner[
"space_hash"] != expected_hash
470 raise ValueError(
"recorded space_hash does not match space")
472 if parent_space
is not None and canonical_space(parent_space) != expected:
473 raise ValueError(
"search space or objective differs (space_hash)")
474 if parent_space
is not None:
475 verify_hash(document)
478 completed = feasible = next_id = 0
480 space = trial.get(
"space", parent_space)
483 "missing recorded space; compatibility cannot be proven"
485 if canonical_space(space) != expected:
486 raise ValueError(
"search space or objective differs (space_hash)")
488 status = trial[
"status"]
489 if status
not in _STATUSES:
490 raise ValueError(f
"unknown trial status: {status}")
491 identifier = trial[
"trial_id"]
492 if type(identifier)
is not int
or not 0 <= identifier < _UINT64_MAX:
493 raise ValueError(
"expected uint64 trial_id with a continuation ID")
494 if identifier
in identifiers:
495 raise ValueError(
"duplicate trial_id")
496 identifiers.add(identifier)
497 next_id = max(next_id, identifier + 1)
498 values = _parameters(study, trial[
"parameters"])
499 _validate_data_context(trial, data_context, required=
True)
500 if data_context
is not None:
501 data_context.validate_row(trial, values)
502 tried.add(_dump({name: _scalar(value)
for name, value
in values.items()}))
503 is_feasible = trial[
"feasible"]
504 objective = trial[
"objective"]
505 if type(is_feasible)
is not bool
or is_feasible != (status ==
"ok"):
506 raise ValueError(
"inconsistent status/feasibility/objective")
507 if objective
is not None and (
508 type(objective)
not in {int, float}
or not math.isfinite(objective)
510 raise ValueError(
"objective must be finite or null")
511 for score
in trial.get(
"pruning", {}).get(
"rung_scores", []):
512 if score
is not None and (
513 type(score)
not in {int, float}
or not math.isfinite(score)
515 raise ValueError(
"invalid pruning rung score")
516 completed += status
in {
"ok",
"constraint_violation"}
517 feasible += is_feasible
519 hashlib.sha256(content).hexdigest(),
520 tuple(sorted(trials, key=
lambda trial: trial[
"trial_id"])),
534 raise WarmStartError(f
"warm-start incompatible: {error}")
from error
547 native: str | Path |
None =
None,
548 defer_symbol_feeds: bool =
False,
550 """Inspect sampler history; binary validation stays in the native mmap loader."""
553 from .cli
import _resolve_native
555 process = subprocess.run(
557 str(_resolve_native(native)),
560 str(study.spec_path),
562 str(Path(path).expanduser().resolve()),
564 *([
"--defer-symbol-feeds"]
if defer_symbol_feeds
else []),
570 if process.returncode:
571 message = process.stderr.strip().split(
"warm-start incompatible: ")[-1]
573 return json.loads(process.stdout)
577 "cardinality": count,
578 "tried": len(history.tried),
579 "remaining":
None if count
is None else count - len(history.tried),
580 "space_hash": space_hash(study),
581 "warm_trials": len(history.trials),
582 "next_id": history.next_id,
583 "completed": history.completed,
584 "feasible": history.feasible,
585 "source_sha256": history.source_sha256,
587 except OSError
as error:
588 raise WarmStartError(f
"warm-start incompatible: {error}")
from error
None validate_row(self, Any row, Any parameters)
dict[str, str] row_identity(self, Any parameters)
None validate_owner(self, Any owner, *bool required=False, bool complete=False)