Note

This page renders committed notebook outputs. The Read the Docs build does not execute notebook code.

Orbit, coverage, and materialized translation batches#

Current surface: V0.29.

Purpose#

Flagship invariant-workflow notebook: read grid-point coverage, translation consistency, read-only orbit reports, and materialized orbit batches with provenance.

What you will learn#

  • The field-shift-then-fixed-window convention for coverage.

  • Why coverage is grid-point coverage, not continuous interval measure.

  • How Heat and KdV translation consistency reports stay report-only.

  • How materialized orbit batches preserve duplicate shifts and source/shift provenance.

Required extras#

Core install is enough; Matplotlib is optional for coverage plots.

Expected runtime#

About 1 minute.

Out of scope#

No train/heldout management, no leakage detector, no time translation, no orbit dataset policy, no transformed fields inside reports.

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))
import numpy as np

from notebooks._tutorial_utils import plot_coverage_counts, pretty_json
from pdelie.data import generate_heat_1d_field_batch, generate_kdv_1d_field_batch
from pdelie.derivatives import compute_spectral_fd_derivatives
from pdelie.invariants import (
    build_uniform_translation_orbit_batch,
    compute_periodic_window_coverage,
    diagnose_uniform_translation_consistency,
    summarize_uniform_translation_orbit,
)
from pdelie.reporting import summarize_residual_batch
from pdelie.residuals import HeatResidualEvaluator, KdVResidualEvaluator

DOMAIN_LENGTH = 2.0 * np.pi
CONFIG = {
    "shifts": [0.0, DOMAIN_LENGTH / 64.0, DOMAIN_LENGTH / 8.0, -DOMAIN_LENGTH / 8.0, DOMAIN_LENGTH / 8.0, DOMAIN_LENGTH],
    "windows": [{"start": 0.0, "width": DOMAIN_LENGTH / 4.0}],
}
CONFIG

[1]:
{'shifts': [0.0,
  0.09817477042468103,
  0.7853981633974483,
  -0.7853981633974483,
  0.7853981633974483,
  6.283185307179586],
 'windows': [{'start': 0.0, 'width': 1.5707963267948966}]}

1. Coverage is grid-point coverage under a fixed convention#

Positive shift means: translate the field, then observe a fixed window. In original coordinates, that is the preimage of the fixed window.

[2]:
x = np.linspace(0.0, DOMAIN_LENGTH, 64, endpoint=False)
coverage = compute_periodic_window_coverage(x=x, windows=CONFIG["windows"], shifts=CONFIG["shifts"])
print(pretty_json({
    "coverage_convention": coverage["coverage_convention"],
    "shift_convention": coverage["shift_convention"],
    "coverage_fraction": coverage["coverage_fraction"],
    "duplicate_shifts_count": len(CONFIG["shifts"]),
}))
plot_coverage_counts(coverage, title="Coverage counts with duplicate shift preserved")

{
  "coverage_convention": "preimage_of_fixed_window_under_translation",
  "coverage_fraction": 0.5,
  "duplicate_shifts_count": 6,
  "shift_convention": "field_shift_then_fixed_window"
}
../_images/tutorials_06_orbit_coverage_diagnostics_3_1.png

2. Translation consistency is report-only#

This diagnostic creates transformed fields internally, but returns a JSON-compatible report instead of a transformed dataset.

[3]:
field = generate_heat_1d_field_batch(batch_size=2, num_times=17, num_points=64, seed=660)
evaluator = HeatResidualEvaluator()
consistency = diagnose_uniform_translation_consistency(field, shifts=CONFIG["shifts"], residual_evaluator=evaluator)
print(pretty_json({
    "summary_type": consistency["summary_type"],
    "shift_count": len(consistency["shift_reports"]),
    "all_residual_stability_passed": all(report["residual_stability_passed"] for report in consistency["shift_reports"]),
}, max_chars=2500))

{
  "all_residual_stability_passed": true,
  "shift_count": 6,
  "summary_type": "uniform_translation_consistency"
}

3. The same consistency report works on KdV#

KdV is included here as a stable nonlinear dispersive fixture. This is still translation consistency only, not a new orbit-materialization policy.

[4]:
kdv_field = generate_kdv_1d_field_batch(batch_size=2, seed=661)
kdv_consistency = diagnose_uniform_translation_consistency(
    kdv_field,
    shifts=[0.0, DOMAIN_LENGTH / 8.0, -DOMAIN_LENGTH / 8.0, DOMAIN_LENGTH],
    residual_evaluator=KdVResidualEvaluator(),
)
print(pretty_json({
    "equation": kdv_field.metadata["parameter_tags"]["equation"],
    "summary_type": kdv_consistency["summary_type"],
    "all_period_wrap_passed": all(report["period_wrap_passed"] for report in kdv_consistency["shift_reports"]),
    "all_residual_stability_passed": all(report["residual_stability_passed"] for report in kdv_consistency["shift_reports"]),
}, max_chars=2500))
{
  "all_period_wrap_passed": true,
  "all_residual_stability_passed": true,
  "equation": "kdv_normalized",
  "summary_type": "uniform_translation_consistency"
}

4. A read-only orbit report combines coverage and consistency#

summarize_uniform_translation_orbit(...) supports workflow reporting without constructing augmented data.

[5]:
orbit_report = summarize_uniform_translation_orbit(
    field,
    shifts=CONFIG["shifts"],
    windows=CONFIG["windows"],
    residual_evaluator=evaluator,
    source_field_id="heat_seed_660",
)
print(pretty_json({
    "summary_type": orbit_report["summary_type"],
    "orbit_passed": orbit_report["orbit_passed"],
    "source_field_id": orbit_report["source_field_id"],
    "coverage_fraction": orbit_report["coverage"]["coverage_fraction"],
}))

{
  "coverage_fraction": 0.5,
  "orbit_passed": true,
  "source_field_id": "heat_seed_660",
  "summary_type": "uniform_translation_orbit"
}

5. Materialized orbit batches are explicit data utilities#

Use orbit batches when a downstream method needs actual transformed samples. Keep source and shift indices enabled for auditability.

[6]:
result = build_uniform_translation_orbit_batch(
    field,
    shifts=CONFIG["shifts"],
    source_field_id="heat_seed_660",
)
residual = evaluator.evaluate(result.field, compute_spectral_fd_derivatives(result.field))
residual_summary = summarize_residual_batch(residual)
print(pretty_json({
    "source_shape": list(field.values.shape),
    "orbit_shape": list(result.field.values.shape),
    "ordering": result.report["ordering"],
    "source_indices_head": result.report["source_batch_indices"][:10],
    "shift_indices_head": result.report["shift_indices"][:10],
    "residual_rms": residual_summary["rms_residual"],
    "split_policy_warning": "orbit batches do not decide train/heldout policy",
}, max_chars=3500))

{
  "orbit_shape": [
    12,
    17,
    64,
    1
  ],
  "ordering": "shift_major",
  "residual_rms": 4.688565186433998e-05,
  "shift_indices_head": [
    0,
    0,
    1,
    1,
    2,
    2,
    3,
    3,
    4,
    4
  ],
  "source_indices_head": [
    0,
    1,
    0,
    1,
    0,
    1,
    0,
    1,
    0,
    1
  ],
  "source_shape": [
    2,
    17,
    64,
    1
  ],
  "split_policy_warning": "orbit batches do not decide train/heldout policy"
}

Recap#

Coverage diagnostics, translation consistency, read-only orbit reports, and materialized orbit batches form a layered invariant workflow.

Common pitfalls#

  • Calling grid-point coverage a continuous coverage measure.

  • Forgetting the positive-shift preimage convention.

  • Dropping source/shift provenance before downstream work.

  • Treating orbit batches as a train/test split or leakage policy.

Extension ideas#

  • Add additional windows and compare coverage fractions.

  • Materialize only training data, then verify on a separate held-out field.

  • Combine orbit, coverage, consistency, fit, and verification reports with summarize_invariant_workflow(...).

What to read/run next#

Run 08_downstream_task_template.ipynb to see how orbit materialization can feed a downstream task responsibly.