Note
This page renders committed notebook outputs. The Read the Docs build does not execute notebook code.
Portability round-trips with empirical revalidation#
Current surface: V0.29.
Purpose#
Show that portable generator payloads should be revalidated against the current field and residual target after export/import.
What you will learn#
How canonical
GeneratorFamilymanifests preserve payload meaning.How strict payload normalization differs from compatibility paths.
How
validate_symmetry_candidate(...)turns a round-tripped payload into an empirical report.How malformed or unsupported payloads fail with typed validation errors.
Required extras#
Core install is enough.
Expected runtime#
Under 1 minute.
Out of scope#
No external detector training, no callable candidates, no formula-string execution, no broad interchange standard.
These notebooks are tutorials, not API contracts. Example outputs are runtime summaries, not canonical paper artifacts.
[1]:
from pathlib import Path
import sys
ROOT = Path.cwd()
if not (ROOT / "pyproject.toml").exists():
ROOT = ROOT.parent
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from notebooks._tutorial_utils import confidence_card, print_cards, pretty_json
from pdelie import SchemaValidationError
from pdelie.data import generate_heat_1d_field_batch
from pdelie.portability import coerce_generator_family, export_generator_family_manifest, import_generator_family_manifest
from pdelie.reporting import summarize_generator_fit_diagnostics, summarize_generator_family
from pdelie.residuals import HeatResidualEvaluator
from pdelie.symmetry import fit_translation_generator, validate_symmetry_candidate
CONFIG = {"fit_epsilon": 1e-4, "source_candidate_id": "round_trip_heat_translation"}
CONFIG
[1]:
{'fit_epsilon': 0.0001, 'source_candidate_id': 'round_trip_heat_translation'}
1. Fit once, then package meaning explicitly#
[2]:
field = generate_heat_1d_field_batch(batch_size=4, num_times=33, num_points=64, seed=630)
evaluator = HeatResidualEvaluator()
generator = fit_translation_generator(field, evaluator, epsilon=CONFIG["fit_epsilon"])
fit_summary = summarize_generator_fit_diagnostics(generator)
family_summary = summarize_generator_family(generator)
print(pretty_json({"family": family_summary, "fit": fit_summary}, max_chars=2500))
{
"family": {
"coefficient_shape": [
1,
4
],
"coefficients": [
[
0.9999999999359518,
1.0133800116507564e-05,
-2.0831610610531808e-08,
5.04006296149618e-06
]
],
"diagnostics": {
"basis": [
"1",
"t",
"x",
"u"
],
"basis_delta_norms": {
"1": 0.0037811302102881963,
"t": 156.86688686928323,
"u": 111.92166664831211,
"x": 3893.915837793674
},
"condition_number": 1149085.046098012,
"design_column_norms": {
"1": 0.0037811302102881963,
"t": 156.86688686928323,
"u": 111.92166664831211,
"x": 3893.915837793674
},
"evidence_label": "direct_svd_in_tolerance",
"fallback_reason": null,
"fit_mode": "svd",
"fit_residual": 0.0033887146157163994,
"min_delta_basis": "1",
"reference_fallback_used": false,
"selected_coefficients": [
0.9999999999359518,
1.0133800116507564e-05,
-2.0831610610531808e-08,
5.04006296149618e-06
],
"selected_span_distance": 1.1317975676651596e-05,
"singular_values": [
3893.9212904134856,
156.89414953360733,
111.69351544041169,
0.0033887146157163994
],
"svd_coefficients": [
0.9999999999359518,
1.0133800116507564e-05,
-2.0831610610531808e-08,
5.04006296149618e-06
],
"svd_span_distance": 1.1317975676651596e-05,
"training_epsilon": 0.0001
},
"fallback_reason": null,
"fit_mode": "svd",
"generator_names": null,
"normalization": "l2_unit",
"parameterization": "polynomial_translation_affine",
"reference_fallback_used": false,
"summary_schema_version": "0.1",
"summary_type": "generator_family",
"translation_span_distance": 1.1317975676651596e-05
},
"fit": {
"basis": [
"1",
"t",
"x",
"u"
],
"basis_delta_norms": {
"1": 0.0037811302102881963,
"t": 156.86688686928323,
"u": 111.92166664831211,
"x": 3893.915837793674
},
"condition_number": 1149085.046098012,
"design_column_norms": {
"1": 0.0037811302102881963,
"t": 156.86688686928323,
"u": 111.92166664831211,
"x": 3893.915837793674
},
"evidence_label": "direct_svd_in_tolerance",
"fallback_reason": null,
"fit_mode": "svd",
"fit_residual": 0.0033887146157163994,
"min
... <truncated 777 chars>
2. Manifest round-trip preserves the canonical generator payload#
[3]:
manifest = export_generator_family_manifest(
generator,
pdelie_version="tutorial-v0.28",
provenance={"notebook": "03_portability_round_trips"},
)
imported = import_generator_family_manifest(manifest)
coerced = coerce_generator_family(manifest)
print(pretty_json({
"manifest_type": manifest["manifest_type"],
"imported_equal": imported.to_dict() == generator.to_dict(),
"coerced_equal": coerced.to_dict() == generator.to_dict(),
}))
{
"coerced_equal": true,
"imported_equal": true,
"manifest_type": "pdelie.generator_family_export"
}
3. Revalidate after import#
This is the v0.16 upgrade: an external or round-tripped candidate should be validated under the current field, residual evaluator, thresholds, and optional reference.
[4]:
validation = validate_symmetry_candidate(
field,
imported,
residual_evaluator=evaluator,
source_candidate_id=CONFIG["source_candidate_id"],
)
card = confidence_card(label="round-tripped generator", fit=fit_summary, validation=validation)
print_cards([card])
print(pretty_json(validation, max_chars=3500))
[
{
"candidate_kind": "generator_family",
"condition_number": 1149085.046098012,
"evidence_label": "direct_svd_in_tolerance",
"fit_mode": "svd",
"label": "round-tripped generator",
"reference_fallback_used": false,
"selected_span_distance": 1.1317975676651596e-05,
"singular_value_count": 4,
"svd_span_distance": 1.1317975676651596e-05,
"validation_conclusion": "validated"
}
]
{
"candidate_kind": "generator_family",
"candidate_summary": {
"coefficient_shape": [
1,
4
],
"coefficients": [
[
0.9999999999359518,
1.0133800116507564e-05,
-2.0831610610531808e-08,
5.04006296149618e-06
]
],
"diagnostics": {
"basis": [
"1",
"t",
"x",
"u"
],
"basis_delta_norms": {
"1": 0.0037811302102881963,
"t": 156.86688686928323,
"u": 111.92166664831211,
"x": 3893.915837793674
},
"condition_number": 1149085.046098012,
"design_column_norms": {
"1": 0.0037811302102881963,
"t": 156.86688686928323,
"u": 111.92166664831211,
"x": 3893.915837793674
},
"evidence_label": "direct_svd_in_tolerance",
"fallback_reason": null,
"fit_mode": "svd",
"fit_residual": 0.0033887146157163994,
"min_delta_basis": "1",
"reference_fallback_used": false,
"selected_coefficients": [
0.9999999999359518,
1.0133800116507564e-05,
-2.0831610610531808e-08,
5.04006296149618e-06
],
"selected_span_distance": 1.1317975676651596e-05,
"singular_values": [
3893.9212904134856,
156.89414953360733,
111.69351544041169,
0.0033887146157163994
],
"svd_coefficients": [
0.9999999999359518,
1.0133800116507564e-05,
-2.0831610610531808e-08,
5.04006296149618e-06
],
"svd_span_distance": 1.1317975676651596e-05,
"training_epsilon": 0.0001
},
"fallback_reason": null,
"fit_mode": "svd",
"generator_names": null,
"normalization": "l2_unit",
"parameterization": "polynomial_translation_affine",
"reference_fallback_used": false,
"summary_schema_version": "0.1",
"summary_type": "generator_family",
"translation_span_distance": 1.1317975676651596e-05
},
"check_reports": {
"finite_transform_verification": {
"report": {
"classification": "exact",
"diagnostics": {
"batch_errors": [
[
4.517438082004002e-09,
1.353233245421291e-09,
2.3268666014623027e-09,
3.82848018646044e-09
],
[
1.4285392566444045e-08,
4.279297660259322e-09,
7.358197057645651e-09,
1.210671752275741e-08
],
[
4.5174362945667467e-08,
1.3532322619707901e-08,
2.3268653770340417e-08,
3.8284788951846757e-08
],
[
1.428533957488756e-07,
4.279281848829128e-08,
7.358170221721434e-08,
1.2106673250724294e-07
],
[
4.517268706859453e-07,
1.3531826537297098e-07,
2.3267817036313517e-07,
3.828339691538713e-07
],
[
1.4280042110990248e-06,
4.277713700327642e-07,
7.355526036720196e-07,
1.2102272040723458e-06
],
[
4.5005338798622695e-06,
1.3482287971829435e-06,
2.3184288082321497e-06,
3.8144362696403565e-06
]
],
"heldout_initial_conditions": 4,
"span_distance": 1.1317975676651596e-05,
"span_tolera
... <truncated 2007 chars>
4. Malformed payloads should fail before scientific interpretation#
External interchange should be strict. A malformed mapping is not a weak candidate; it is not a candidate at all.
[5]:
malformed_payload = {"schema_version": "0.2", "parameterization": "polynomial_translation_affine"}
try:
validate_symmetry_candidate(
field,
malformed_payload,
residual_evaluator=evaluator,
source_candidate_id="malformed_payload",
)
except SchemaValidationError as exc:
print(type(exc).__name__, str(exc))
SchemaValidationError GeneratorFamily candidate payload is malformed.
Recap#
Export/import preserves payloads, while candidate validation checks whether the imported object is credible for the current field and residual evaluator.
Common pitfalls#
Assuming deserialization means scientific validity.
Mixing canonical payloads with unsupported legacy or partial mappings.
Skipping revalidation after changing fields, residual evaluators, or thresholds.
Extension ideas#
Attach provenance to manifests from an external detector.
Validate the same payload against Heat and Fisher-KPP to see equation-specific behavior.
Add stricter checks in your own workflow before downstream acceptance.
What to read/run next#
Run 04_discovered_vs_known_translation_generators.ipynb for side-by-side candidate comparison.