Exact local subdivision ======================= When to split a model --------------------- Subdivision replaces one fitted cell by two local Bézier models whose images together reproduce the parent. Use it to isolate a region for inspection, evaluate pieces in separate downstream systems, or prepare a later local refinement. It is useful when one global model spans regions with different geometric scales and you want explicit charts for those regions. Subdivision itself is an exact change of parameterization. It does not collect observations, optimize coefficients, change polynomial degree, or reduce prediction error. If a parent underfits a localized feature, its exact children still underfit that feature. Error reduction requires an additional refitting step, usually with new observations or independently adjustable piecewise coefficients. Keep this distinction visible in before/after plots. ``subdivide`` acts on one selected cell. It returns a ``SubdivisionResult`` with two models and two parameter maps. It does not automatically update neighboring cells of an assembled domain, rebuild shared topology, or create a globally remeshed model archive. The maps retain the selected parent cell ID so that evaluation can be traced back to the original model. A complete exact split ---------------------- The example fits a scalar quadratic on a segment and splits the parameter edge at fraction .4. Each child has its own full local barycentric domain. The map converts those local points back to the appropriate parent interval. .. testcode:: splitting import torch from torch_bsf import BezierSpace, Samples, fit from torch_bsf.sampling import grid from torch_bsf.splitting import EdgeSplit, LongestEdge, subdivide space = BezierSpace.simplex(1, degree=2) observations = grid(space, 8) x = observations.parameters[:, 1] model = fit(space, Samples(observations, x.square()[:, None])).require_success().model pieces = subdivide(model, cell=0, policy=EdgeSplit(0, 1, fraction=.4)) for child, mapping in zip(pieces.models, pieces.maps, strict=True): child_points = grid(child.space, 40) parent_points = mapping.to_parent(child_points) assert torch.allclose(child(child_points), model(parent_points), atol=1e-12) assert len(pieces.models) == len(pieces.maps) == 2 The selected parent is unchanged. Child coefficient tensors retain its dtype and device, while each child receives a single-cell space. The representation is local even if the original model was assembled from many cells. Always pair each child with its corresponding map; swapping maps produces an incorrect comparison despite identical coordinate widths. .. figure:: ../_static/guides/subdivision.png :width: 100% :alt: A parent curve and two child curves displayed in the same parent parameter coordinate. Before: one fitted cubic. After: two exact pieces at fraction .4, plotted against parent coordinates. The curves join without changing the represented response. The generator checks maximum child/parent discrepancies below 1e-12; the figure does not claim a reduction in fitting error. The figure uses the noisy-data cubic from ``examples/generate_guide_figures.py``, while the executable example above uses an exactly representable quadratic. The same map invariant applies in both cases. The figure's measured errors are stored in :download:`metrics.json <../_static/guides/metrics.json>`. Understand the split fraction ----------------------------- For an edge joining factor vertices :math:`v_i` and :math:`v_j`, fraction :math:`s` inserts a new vertex .. math:: v_{\mathrm{new}}=(1-s)v_i+s v_j,\qquad 0