+
    nDj                        R t ^ RIt^ RIHt ^ RIHtHtHtH	t	 ^RI
Ht ^ RIHt ^ RIHt R"R ltR t ! R	 R
4      tR tR#R lt ! R R4      tRRRRR^(/R ltR tR tR$R ltRtR tR^R^RRR^2RRRRRR/R  ltRRR^R^RRR^2/R! ltR# )%u  
Regrid (2-D smoothing B-splines via separable 1-D FITPACK kernels)
==================================================================

1) Overview
-----------
This module fits a bivariate tensor-product B-spline surface to gridded data
`Z[i, j]` sampled on strictly increasing coordinates `x[i]`, `y[j]`. It mirrors
the adaptive spirit of FITPACK's REGRID/fpgrre (knot growth + smoothing
parameter search to meet a target residual `s`) while keeping the control flow
explicit in Python and returning a 2-D `NdBSpline`.

**Key conventions**
- **Penalty scaling is 1/p** - *same as FITPACK*. The augmented system stacks
  `(D / p)` under the data matrix. Smaller `p` -> stronger smoothing; larger `p`
  -> weaker smoothing (approaching interpolation).
- `p == -1` is used **as a sentinel for `p = ∞`** (interpolatory limit): when
  seen, the solver omits penalty rows entirely.

2) Mathematics (and how this differs from FITPACK's REGRID)
-----------------------------------------------------------
Let `A_x`, `A_y` be banded 1-D design matrices and `D_x`, `D_y` the banded
1-D roughness (difference) matrices returned by FITPACK/Dierckx's 1-D APIs
(`data_matrix`, `disc`). The 2-D smoothing objective is

    minimize  ||A c - z||^2 + (1/p) ||D c||^2,            (1)

implemented by the **augmented** least squares system

    [ A ] c ~ [ z ]
    [D/p]     [ 0 ].

**Same as FITPACK:** Equation (1) uses the *1/p* convention.
**Different in this module:** We realize the 2-D problem by composing **1-D**
banded operators in two separable passes (x then y), instead of calling a
monolithic 2-D routine. This makes the mathematics transparent while producing
the same normal equations structure that REGRID targets internally.

3) Residual energy: definition and use
--------------------------------------
After solving on the current knots (initially at the interpolatory limit,
`p = ∞`), we evaluate the surface and form

    R = Z - Zhat,                         fp = sum(R[i, j]^2).

We then **project** residual energy to knot spans along each axis:

- Row energy  `row_energy[i] = sum(R[i, j]^2, j)`  -> accumulated into `fpintx`.
- Column energy `col_energy[j] = sum(R[i, j]^2, i)` -> accumulated into `fpinty`.

These per-span energies guide **adaptive knot insertion**: the algorithm picks
high-energy spans (skipping zero-length ones) and inserts data-aligned knots
(e.g., median sample within the span), with simple batch sizing and headroom
limits. This is conceptually the same idea as REGRID's `fpint` arrays; here it
is implemented explicitly and vectorized in Python for clarity.

4) Solver used here vs. FITPACK's REGRID
----------------------------------------
**This module (separable 1-D composition):**
- Uses **1-D FITPACK/Dierckx kernels** (`data_matrix`, `disc`, `qr_reduce`,
  `fpback`) to solve 2-D via two passes:
  1) augment/QR/backsolve along **x**, producing an intermediate;
  2) augment/QR/backsolve along **y**, producing the coefficient grid `C`.
- The augmented rows are stacked as `[A; D/p]` (1/p scaling, **same as FITPACK**).
- If `fp > s` after knot growth, performs a **scalar search in `p`** using a
  ratio-of-roots routine; `p == -1` is treated as `p = ∞` for the interpolatory
  reference without penalty rows.

**FITPACK REGRID (monolithic 2-D routine):**
- Implements the same mathematical objective and **1/p** penalty scaling,
  but inside a specialized, fully 2-D Fortran routine with in-place Givens/QR
  and a rational update for `p`.
- Handles residual partitioning, knot insertion, and `p` updates internally.

**Practical difference:** We build the 2-D solve from **1-D building blocks**,
which keeps each stage observable/testable and uses the same low-level kernels
as FITPACK, but without relying on the single monolithic REGRID entry point.

5) Execution flow (who calls whom)
----------------------------------

**Top-level**
1. `_regrid`
   - Validates shapes/monotonicity and normalizes `bbox`.
   - Dispatches to `_regrid_fitpack(...)`.

**Core driver**
2. `_regrid_fitpack`
   - Clips inputs to `bbox` via `_apply_bbox_grid` -> `(x_fit, y_fit, Z_fit)`.
   - If `s == 0` (interpolation path):
     - Build initial not-a-knot vectors: `tx = _not_a_knot(x_fit, kx)`,
       `ty = _not_a_knot(y_fit, ky)`.
     - Build design matrices: `(Ax, Ay, Q) = build_design_matrices(...)`.
     - Solve once at `p = -1` (inf): `C0, fp = _solve_2d_fitpack(...)`.
     - Return `return_NdBSpline(...)`.
   - Else (`s > 0`, smoothing with adaptive knots):
     - Initialize clamped no-interior-knot vectors with
       `_initialise_knots` (for x and y).
     - **Knot-growth loop** (up to `len(x_fit)+len(y_fit)` iterations):
       1) Build design matrices: `(Ax, Ay, Q) = build_design_matrices(...)`.
       2) Interpolatory reference solve (`p = -1`): `C0, fp = _solve_2d_fitpack(...)`.
       3) If both `tx`, `ty` are at minimal size, record `fp0 = fp`
          (used to bracket the p-search).
       4) If `fp < s`: **stop growth** and proceed to p-search / finalize.
       5) Compute residuals for knot insertion:
          - Convert packed to CSR once per axis for evaluation:
            `_Ax = Ax.tocsr(...)`, `_Ay = Ay.tocsr(...)`
          - Evaluate `Z0 = _Ax @ C0 @ _Ay.T`, residual `R = Z_fit - Z0`.
       6) Insert knots on alternating axes using energy heuristics:
          - If last axis was `y`, grow `tx` with
            `_add_knots(..., residuals=np.sum(R**2, axis=1), ...)`.
          - Else, grow `ty` with `_add_knots(..., residuals=np.sum(R**2, axis=0), ...)`.
          - Update `fpold`, `nplus{ x|y }`, `last_axis`.
     - If growth ended with both axes still minimal: return `return_NdBSpline(...)`.
     - **Finite-p smoothing (if needed):**
       - Build 1-D penalty operators: `(Drx, _, ncx) = disc(tx, kx)`,
         `(Dry, _, ncy) = disc(ty, ky)`, wrap as `PackedMatrix`.
       - Rebuild design matrices on the final `(tx, ty)`.
       - Run `_p_search_hit_s(...)` to find `p*` such that `fp(p*) ~ s`:
         - Internally constructs `F(...)` which evaluates `fp(p)`
           by calling `_solve_2d_fitpack(...)`.
         - Uses `root_rati` on `g(p) = fp(p) - s` with the interpolatory
           reference at `p = inf` and `fp0` bracket.
       - Return `return_NdBSpline(...)`.

**Separable solver (used by both interpolation and p-search)**
3. `_solve_2d_fitpack`
   - Forms augmented banded systems via `_stack_augmented_fitpack`:
     - X-side: `Ax_aug = [Ax; Dx/p]` if `p != -1`, else just `Ax`.
     - Y-side: `Ay_aug = [Ay; Dy/p]` if `p != -1`, else just `Ay`.
     - Pads RHS `Q` with zeros when penalties are stacked.
   - **Stage X**: `_dierckx.qr_reduce(Ax_aug, ...)` then
     `_dierckx.fpback(...)` -> intermediate `T`.
   - Transpose `T` to feed Y solves column-wise; pad if needed.
   - **Stage Y**: `_dierckx.qr_reduce(Ay_aug, ...)` then
     `_dierckx.fpback(...)` -> coefficients `C`.
   - Build CSR design matrices once: `_Ax = Ax.tocsr(...)`, `_Ay = Ay.tocsr(...)`.
   - Evaluate `zhat = _Ax @ C.T @ _Ay.T`, compute `fp = ||Z_fit - zhat||^2`.
   - Return `C.T` (in `(nx_coef, ny_coef)` layout) and `fp`.

**Helpers**
- `_apply_bbox_grid(...)` - slices to `(x_fit, y_fit, Z_fit)`.
- `build_design_matrices(...)` - wraps `_dierckx.data_matrix` and
  returns `PackedMatrix` wrappers + `Q`.
- `_initialise_knots(...)`, `_add_knots(...)` - FITPACK-style knot bookkeeping/growth.
- `disc(...)` - 1-D roughness operators (packed band form).
- `F` + `root_rati` - maps `p -> fp(p)` and finds `p*` with `fp(p*) ~ s`.
- `return_NdBSpline(...)` - packs `(tx, ty, C)` into an `NdBSpline`.
N)	NdBSpline)	root_ratidiscadd_knot_not_a_knot)_dierckx)	csr_array)_validate_intc                   \        V P                  4      ^8w  d   \        R4      h\        VR4      p\        VR4      pV^ 8  g   V^ 8  d   \        R4      hV P                  P
                  R,          p\        P                  ! V4      p\        P                  ! V4      pV'       EdW   VP                  ^ 8X  g   VP                  ^ 8X  dL   \        P                  ! VP                  VP                  3V,           V P                  P                  R7      pV# VP                  ^8  d?   \        P                  ! \        P                  ! V4      R8  4      '       g   \        R4      hVP                  ^8  d?   \        P                  ! \        P                  ! V4      R8  4      '       g   \        R	4      h\        P                  ! WR
R7      w  r\        P                  ! W3RR7      p
V ! WV3V P                  R7      pV# VP
                  VP
                  8w  d   \        P                   ! W4      w  rVP                  ^ 8X  d>   \        P                  ! VP
                  V,           V P                  P                  R7      # \        P                  ! VP#                  4       VP#                  4       3RR7      p
V ! WV3V P                  R7      pVP%                  VP
                  V,           4      # )a  
Evaluate a 2D `NdBSpline` like a classical bivariate API.

Parameters
----------
ndbs : NdBSpline
    A 2D spline object (``len(ndbs.t) == 2``).
x, y : array_like
    Sample locations. If ``grid=True``, these must be 1-D strictly
    increasing vectors. If ``grid=False``, they can be broadcastable
    arrays of the same shape.
dx, dy : int, optional
    Derivative orders along `x` and `y` respectively, by default 0.
grid : bool, optional
    If True, evaluate on the cartesian product of `x` and `y`;
    otherwise treat `(x, y)` as paired coordinates, by default True.

Returns
-------
ndarray or (ndarray, dict)
    Evaluated values with shape:
    - ``(len(x), len(y), ...)`` if ``grid=True``.
    - ``x.shape + ...`` if ``grid=False``.

Raises
------
ValueError
    If `ndbs` is not 2D, derivatives are negative, or monotonicity checks fail.

Notes
-----
This is a thin convenience wrapper around ``NdBSpline.__call__`` with input
validation and optional profiling.
z*ndbs must be a 2D NdBSpline (len(t) == 2).dxdyz,order of derivative must be positive or zero:   NNdtype        z1x must be strictly increasing when `grid` is Truez1y must be strictly increasing when `grid` is Trueij)indexingaxis)nuextrapolate)lent
ValueErrorr	   cshapenpasarraysizezerosr   alldiffmeshgridstackr   broadcast_arraysravelreshape)ndbsxyr   r   gridtrailingvalsXYxis   &&&&&&     S/data/cameron/venvs/s3viz/lib/python3.14/site-packages/scipy/interpolate/_regrid.py_ndbspline_call_like_bivariater2      s   F 466{aEFF	r4	 B	r4	 B	AvaGHHvv||BH


1A


1At66Q;!&&A+88QVVQVV,x7tvv||LDKFFaK"&&s):";";PQQFFaK"&&s):";";PQQ{{1$/XXqf2&B81A1AB77agg&&q,DA66Q;88AGGh.dffllCCXXqwwy!''),26B81A1AB||AGGh.//    c                    \        V^ ,          4      \        V^,          4      rCVw  rVV^,          P                  W5,
          ^,
          WF,
          ^,
          4      p\        V^ ,          V^,          3Wr4      # )aO  
Build a 2D ``NdBSpline`` from knot vectors and a coefficient grid.

Parameters
----------
fp : float
    Residual sum of squares of the produced fit (kept for upstream use).
tck : tuple
    Tuple ``(tx, ty, C)`` where ``tx``, ``ty`` are knot vectors and ``C``
    is a coefficient array with shape ``(nx - kx - 1, ny - ky - 1)`` or
    a compatible shape that can be reshaped to that.
degrees : tuple of int
    Degrees ``(kx, ky)`` along x and y.

Returns
-------
NdBSpline
    The constructed 2D spline.

Notes
-----
Only repacks the coefficient grid; ``fp`` is not used internally here.
)r   r'   r   )fptckdegreesnxnykxkyr   s   &&&     r1   return_NdBSpliner<      s\    0 Q[#c!f+FBArw{BGaK0Ac!fc!f%q22r3   c                   F   a  ] tR tRt o RtR t]R 4       tR tR t	Rt
V tR# )	PackedMatrixi  a_  A simplified CSR format for when non-zeros in each row are consecutive.

Assuming that each row of an `(m, nc)` matrix 1) only has `nz` non-zeros, and
2) these non-zeros are consecutive, we only store an `(m, nz)` matrix of
non-zeros and a 1D array of row offsets. This way, a row `i` of the original
matrix A is ``A[i, offset[i]: offset[i] + nz]``.

c                *    Wn         W n        W0n        R # N)aoffsetnc)selfrA   rB   rC   s   &&&&r1   __init__PackedMatrix.__init__  s    r3   c                T    V P                   P                  ^ ,          V P                  3# )    )rA   r   rC   )rD   s   &r1   r   PackedMatrix.shape  s    vv||A''r3   c                   \         P                  ! V P                  4      pV P                  P                  ^,          p\	        VP                  ^ ,          4       Fw  p\        V P                  V P                  V,          ,
          V4      pV P                  VRV13,          WV P                  V,          V P                  V,          V,           13&   Ky  	  V# )   N)r   r    r   rA   rangeminrC   rB   )rD   outneleminels   &    r1   todensePackedMatrix.todense  s    hhtzz"Qsyy|$AdggA.6C:>&&DSD/C4;;q>$++a.3"6667 % 
r3   c                   V P                   P                  4       p\        P                  ! V P                  V^,           4      P                  RV^,           4      pV\        P                  ! V^,           V P                  P                  R7      ,           pVP                  4       p\        P                  ! ^ V^,           V^,           ,          V^,           V P                  P                  R7      p\        WEV3W#V,
          ^,
          3R7      # )rK   r   )r   r   )	rA   r&   r   repeatrB   r'   aranger   r   )rD   kmlen_tdataindicesindptrs   &&&&   r1   tocsrPackedMatrix.tocsr   s    vv||~ ))DKK1-55b!A#>BIIac1B1BCC--/1q1uQ/Q!%!2!24 F#ai!m$ 	r3   )rA   rC   rB   N)__name__
__module____qualname____firstlineno____doc__rE   propertyr   rR   r]   __static_attributes____classdictcell____classdict__s   @r1   r>   r>     s2     
 ( ( r3   r>   c                   VR8X  d6   V P                   P                  4       V P                  P                  4       V3# V^,           p\        P                  ! W!P
                  ^ ,          ,           V^,           3\        R7      pV P                   RV1R3,          VRV1RV13&   VP                   V,          WbR1R3&   \        P                  ! V P                  VP                  34      pWgV3# )a  
Builds augmented banded matrix.

Parameters
----------
A : PackedMatrix
    Banded data/design matrix for one axis (from `_dierckx.data_matrix`).
D : PackedMatrix
    Banded roughness (difference) penalty matrix for the same axis
    (from `disc`).
nc : int
    Number of top (data) rows from `A` to include.
k : int
    Spline degree (used only for sizing in the current implementation).
p : float
    Smoothing parameter. The effective penalization term is scaled as **1/p**:
    larger `p` means *less* smoothing (approaching interpolation).
    If `p == -1`, it signals *p -> inf*, i.e. a pure interpolatory system
    with **no** penalty rows appended.

Returns
-------
AA : ndarray
    Augmented banded matrix with `A` stacked over `(D / p)` when `p != -1`.
offset : ndarray
    Concatenated band offsets for the augmented matrix.
nc : int
    Returned unchanged for downstream convenience.
r   N:NNNr   )rA   copyrB   r   r    r   floatconcatenate)ADrC   rW   pnzAArB   s   &&&&&   r1   _stack_augmented_fitpackrr   2  s    < 	Bwssxxz188==?B..	
QB	2
?AE*%	8B33ssAv;BssCRCxLqBsAvJ^^QXXqxx01Fr>r3   c                &   \         P                  ! V4      p\         P                  ! V	4      p\        WV P                  ^ ,          WC4      w  pppV P                  p\        WVP                  ^ ,          Ws4      w  pppVP                  pVR8w  dW   \         P
                  ! V\         P                  ! VP                  ^ ,          VP                  ^,          3\        R7      .4      p\        P                  ! VVVV4       \        P                  ! VVVW%WMVR4	      w  p p\         P                  ! VP                  4      pVR8w  dW   \         P
                  ! V\         P                  ! VP                  ^ ,          VP                  ^,          3\        R7      .4      p\        P                  ! VVVV4       \        P                  ! VVWWVVR4	      w  pppV P                  WFP                  ^ ,          \        V4      4      pVP                  WyP                  ^ ,          \        V4      4      pVVP                  ,          VP                  ,          pV
V,
          p\         P                  ! \         P                   ! V4      4      pVP                  VV3# )a  
Solve the 2-D tensor-product spline system using separable banded QR.

================================================================
Mathematical model (step by step, plain text)
================================================================

Shapes:
    Z      : (mx, my)  -> original data
    Ax, Ay : design matrices for x and y
    Dx, Dy : roughness penalty matrices for x and y
    C      : (nx, ny)  -> spline coefficients to solve for

Surface approximation:
    Zhat = Ax * C * Ay^T

Objective (smoothing formulation):
    minimize ||Ax*C*Ay^T - Z||^2 + (1/p)*(||Dx*C||^2 + ||C*Dy^T||^2)

In practice (FITPACK-style separable approach), we solve this in two stages:

--------------------------------------------------------
Stage 1 (x-direction solve for all y-columns together):
--------------------------------------------------------

    For each column of Z:
        minimize ||Ax*T - Z||^2 + (1/p)*||Dx*T||^2

    This is equivalent to the augmented least-squares system:
        [Ax]       [Z]
        [Dx/p] * T = [0]

    i.e.  minimize || [Ax; Dx/p]*T - [Z; 0] ||^2

    The solution T is obtained by QR reduction and back-substitution.

--------------------------------------------------------
Stage 2 (y-direction solve using transposed result):
--------------------------------------------------------
    Now treat T^T as the new RHS for the y-direction:
        minimize ||Ay*C^T - T^T||^2 + (1/p)*||Dy*C^T||^2

    Equivalent to augmented system:
        [Ay]       [T^T]
        [Dy/p] * C^T = [0]

    i.e.  minimize || [Ay; Dy/p]*C^T - [T^T; 0] ||^2

    Solving this gives C^T (then transposed back to C).

--------------------------------------------------------
Interpolation limit:
--------------------------------------------------------
    If p == -1, penalties are omitted (Dx, Dy are not stacked).
    The solver behaves as a near-interpolating system.

--------------------------------------------------------
Residual computation:
--------------------------------------------------------
    Zhat = Ax * C * Ay^T
    R    = Z - Zhat
    fp   = sum(R^2)

Parameters
----------
Ax, Ay : PackedMatrix
    Banded data matrices for the x and y axes.
Q : ndarray, shape (mx, my)
    RHS data grid (copied from `Z`).
p : float
    Smoothing parameter. The penalty term is scaled as **1/p**.
    Setting `p == -1` signals *p -> inf* (interpolation, omit penalty).
kx, ky : int
    Spline degrees along x and y.
tx, ty : ndarray
    Knot vectors along x and y.
x_x, x_y : ndarray
    Sample coordinates.
z : ndarray
    Original data grid for residual evaluation.
Dx, Dy : ndarray
    Banded roughness penalty matrices for x and y.
    Optional, Only needed when ``p != -1``.

Returns
-------
C : ndarray
    2-D B-spline coefficient grid.
fp : float
    Residual sum of squares between fitted surface and `z`.
R : ndarray, shape (mx, my)
    Residual matrix ``z - zhat``, where ``zhat = Ax @ C @ Ay.T``.

Notes
-----
This performs two separable QR solves (x then y), each augmented by
`(D / p)` when `p != -1`.  Setting `p = -1` skips all penalty rows,
yielding an interpolatory surface.  The resulting coefficients and residual
follow the same conventions as FITPACK's `fpgrre`.
r   Fr   )r   	ones_likerr   r   rC   vstackr    rk   r   	qr_reducefpbackascontiguousarrayTr]   r   sumsquare)AxAyQro   r:   txx_xr;   tyx_yzDxDyw_xw_yAx_augoffset_aug_xnc_augxnc_xAy_augoffset_aug_ync_augync_yry   _Cr5   _Ax_AyzhatRs   &&&&&&&&&&&&&                  r1   _solve_2d_fitpackr   Z  s   R ,,s
C
,,s
C %=
R%$!FL'55D %=
R%$!FL'55D 	Bw IIq"((BHHQK#<EJKL v|Wa8 ooc	r	5GAq! 	QSS!A 	BwIIq"((BHHQK#<EJKL v|Wa8 		HAq" ((2yy|SW
-C
((2yy|SW
-C 9suuD
 	
DA			!	B 33A:r3   c                   0   a  ] tR tRt o RtR tR tRtV tR# )Fi&  a#  
Callable wrapper for computing `fp(p)` for a fixed spline configuration.

Parameters
----------
Ax, Ay : PackedMatrix
    Banded data matrices.
Dx, Dy : PackedMatrix
    Banded penalty matrices.
kx, ky : int
    Degrees along x and y.
tx, ty : ndarray
    Knot vectors along x and y.
x_x, x_y : ndarray
    Sample coordinates.
w_x, w_y : ndarray
    Weights (usually ones).
z : ndarray
    Data grid for computing the residual.

Attributes
----------
C : ndarray
    Coefficient matrix from the most recent solve.
fp : float
    Residual value from the most recent solve.

Notes
-----
The penalty is applied as **1/p**, so smaller `p` values yield heavier
smoothing. Setting `p == -1` corresponds to *p = inf*, i.e. interpolation.
Intended for use by `_p_search_hit_s` to iteratively evaluate `fp(p)`.
c                    Wn         W n        W0n        W@n        WPn        W`n        Wpn        Wn        Wn        Wn	        Wn
        Wn        R # r@   )r|   r   r}   r   r~   r:   r   r   r;   r   r   r   )rD   r|   r   r}   r   r~   r:   r   r   r;   r   r   r   s   &&&&&&&&&&&&&r1   rE   
F.__init__I  s@     r3   c                ^   \        V P                  V P                  V P                  P	                  4       WP
                  V P                  V P                  V P                  V P                  V P                  V P                  V P                  V P                  R 7      w  r#pW n        W0n        V# ))r   r   )r   r|   r}   r~   rj   r:   r   r   r;   r   r   r   r   r   r   r5   )rD   ro   r   r5   r   s   &&   r1   __call__
F.__call__Y  su    
 %GGTWWdffkkmwwGGTWWdhhww477	$q
 	r3   )r|   r}   r   r   r   r~   r5   r:   r;   r   r   r   r   r   N)	r_   r`   ra   rb   rc   rE   r   re   rf   rg   s   @r1   r   r   &  s      D  r3   r   p_initg      ?tol_relgMbP?maxitc                 aa \        WW#WEWgWW4      oVV3R lpV! R4      pRVS,
          3\        P                  V33p\        SV,          R4      p\	        VVVVVR7      pVP
                  pS! V4      pSP                  pVVV3# )a(  
Search for a smoothing parameter `p` such that `fp(p) ~ s`.

Parameters
----------
Ax, Ay : PackedMatrix
    Banded data matrices.
Dx, Dy : PackedMatrix
    Banded penalty matrices.
Q : ndarray
    RHS data grid (copy of `Z`).
kx, ky : int
    Spline degrees.
tx, ty : ndarray
    Knot vectors.
x_x, x_y : ndarray
    Sample coordinates.
w_x, w_y : ndarray
    Sample weights.
z : ndarray
    Original data grid for residuals.
s : float
    Target smoothing residual (`fp` target).
fp0 : float or None
    Residual at `p = inf` (interpolatory limit,
                           represented by `p == -1`).
p_init : float, optional
    Starting guess for the finite `p` search, default 1.0.
tol_rel : float, optional
    Relative tolerance for matching `fp(p)` to `s`.
maxit : int, optional
    Maximum iterations for the root search.

Returns
-------
p_star : float
    Smoothing parameter for which `fp(p_star)` ~ `s`.
C_star : ndarray
    Coefficient grid corresponding to `p_star`.
fp_star : float
    Residual at `p_star`.

Notes
-----
The solver treats `p == -1` as *p = inf* (interpolatory, no penalty).
For finite `p`, the penalty scales as **1/p** - smaller `p` increases
smoothing. A ratio-of-roots search (`root_rati`) iteratively adjusts `p`
until the residual `fp(p)` matches the target `s` within tolerance.
c                 "   < S! V 4      S,
          # r@    )ro   fp_atss   &r1   g_p_search_hit_s.<locals>.g  s    Qx!|r3   r   g-q=)r   r   )r   r   infmaxr   rootr   )r|   r   r}   r   r~   r:   r   r   r;   r   r   r   r   fp0r   r   r   r   fpmsbracketftolrp_starfp_starC_starr   s   &&&&&&&&&&&&f&$$$        @r1   _p_search_hit_sr   h  s    l bbars'E R5DS1W~~.Gq7{E"D!VWd%8AVVFFmGWWF67""r3   c                b   \        V Uu. uF  qDRJ NK  	  up4      '       d   WV\        R4      \        R4      3# Vw  rVrxWV8  d   Wx8  g   \        R4      h\        P                  ! W8  W8*  ,          4      ^ ,          p	\        P                  ! W8  W8*  ,          4      ^ ,          p
V	P
                  ^ 8X  g   V
P
                  ^ 8X  d   \        R4      hW	,          W,          V\        P                  ! W4      ,          \        P                  V	,          \        P                  V
,          3# u upi )a  
Restrict (x, y, Z) to a rectangular bounding box.

Parameters
----------
x, y : ndarray
    Monotonic sample coordinates.
Z : ndarray, shape (len(x), len(y))
    Data grid.
bbox : sequence of 4 scalars or None
    ``(xb, xe, yb, ye)``; any element may be None to skip clipping.

Returns
-------
x_fit, y_fit, Z_fit : ndarray
    Sliced arrays restricted to bbox.
ix, iy : slice or ndarray
    Indexers mapping from full arrays to the restricted ones.

Raises
------
ValueError
    If bbox is invalid or excludes all samples along an axis.
Nz%bbox must satisfy xb < xe and yb < yez$bbox excludes all samples in x or y.)r!   slicer   r   wherer   ix_s_)r)   r*   Zbboxbboxixbxeybyeixiys   &&&&       r1   _apply_bbox_gridr     s    2 t,teTMt,--QdU4[00NBBG@AA	17qw'	(	+B	17qw'	(	+B	ww!|rww!|?@@5!%266">*BEE"IruuRy@@ -s   D,c                    \         P                  ! V 4      p\         P                  ! V4      p\        P                  ! WWW4      w  rp\        P                  ! WWh4      w  rpVP	                  4       p\        WV4      \        WV4      V3# r@   )r   rt   r   data_matrixrj   r>   )r)   r*   r   r   r   r:   r;   r   r   r|   offset_xr   r}   offset_yr   r~   s   &&&&&&&         r1   _build_design_matricesr     sv    
,,q/C
,,q/C!--aR=B$!--aR=B$	At,t, r3   c                   Vf)   \        W,           ^,           ^V,          ^,           4      pM6V^V^,           ,          8  d"   \        RV: R^V^,           ,           R24      h^V^,           ,          pW,           ^,           p\        P                  ! V.V^,           ,          V.V^,           ,          ,           4      pWtWV3# )a  
Initialize a non-periodic knot vector.

Parameters
----------
m : int
    Number of data points (equivalent to len(x) if x were provided).
xb, xe : float
    Domain endpoints used to seed the initial knot vector with no internal knots.
k : int
    Spline degree.
nest : int, optional
    Storage cap for knots. If None, defaults to max(m + k + 1, 2*k + 3).
    Must satisfy nest >= 2*(k + 1); otherwise a ValueError is raised.

Returns
-------
t : 1-D ndarray
    Initial knot vector with no internal knots: [xb]*(k+1) + [xe]*(k+1).
nest : int
    The finalized storage cap for knots.
nmin : int
    Lower bound on knot count.
nmax : int
    Upper bound on knot count.

What this does
--------------
- Computes defaults and bounds used by FITPACK-style knot growth:
    * nest: storage cap for knots (defaults to max(m + k + 1, 2*k + 3))
    * nmin: minimal knot count (2*(k+1))
    * nmax: maximal knot count (m + k + 1)
- Returns an initial knot vector with no internal knots:
    t = [xb]*(k+1) + [xe]*(k+1)
z`nest` too small: nest = z < 2*(k+1) = .)r   r   r   r   )rX   r   r   rW   nestnminnmaxr   s   &&&&&   r1   _initialise_knotsr     s    H |1519acAg&!QU)9$-1Q3yPQRSSa!e9D519D 	

B41:ac
*+ADr3   c                   V\         ,          pVP                  pWr,
          pW8X  d   ^p
MSW,
          pW8  d   \        W,          V,          4      MV
^,          p\        V
^,          \	        W^,          ^4      4      p
\        V
4       FB  p\        WW4      pVP                  ^ ,          pW8  d   \        W4      V
3u # W8  g   K?  W:3u # 	  W:3# )aE  
Knot-growth helper for knot-finding loop (non-periodic).

Parameters
----------
x : 1-D ndarray
    Strictly increasing sample coordinates.
k : int
    Spline degree.
s : float
    Target smoothing.
t : 1-D ndarray
    Current knot vector to be grown.
nmin, nmax : int
    Lower/upper bounds on knot count (from initialisation).
nest : int
    Storage cap for total knots.
fp, fpold : float
    Current and previous residual sums of squares. Used to update nplus.
residuals : 1-D ndarray
    Most recent residual signal used by `add_knot` to decide placement.
nplus : int
    Previous iteration's proposed number of knots; used to update the next nplus.

Returns
-------
t_new, nplus : tuple
    Updated knot vector and the nplus chosen for this step.
    If n >= nmax, t_new is a not-a-knot layout. If n >= nest, t_new is the
    current vector respecting the storage cap.

What this function does
-----------------------
- Assumes the caller has already decided to GROW (i.e., checks
  like |fp - s| < acc or fp < s has FAILED).
- Updates nplus (how many knots to add next) using the FITPACK heuristic
  based on the previous improvement (delta = fpold - fp).
- Inserts up to nplus new internal knots using `add_knot(x, t, k, residuals)`.
- Stops early if storage or interpolation caps are reached:
    * if n >= nmax: switch to interpolation layout (not-a-knot) and return
    * if n >= nest: return current t respecting storage cap

How it compares with _fitpack_repro.py::_generate_knots_impl
-------------------------------------------------------------
Similarities:
  1) Same growth logic for nplus:
     - Use delta = fpold - fp with ratio fpms/delta
     - Apply min/max caps (doubling and halving behavior)
  3) Same storage guard:
     - If n reaches nest, stop and return current t
  4) Same end behavior at the "interpolating" cap:
     - When n >= nmax, switch to not-a-knot layout and return

Differences:
  1) API style:
     - _generate_knots_impl is a generator that yields trial knot vectors and
       recomputes residuals/fp internally on each iteration.
     - `_add_knots` is a stateful helper that only grows knots; it expects the
       caller to handle residual computation and fp/fpold updates between calls.
  2) Periodicity:
     - _generate_knots_impl supports periodic=True.
     - `_add_knots` is non-periodic only; it uses not-a-knot when n >= nmax.
  3) Residual computation:
     - _generate_knots_impl calls an internal residual routine each iteration.
     - `_add_knots` does not compute residuals; the caller must supply:
         residuals (used by add_knot), fp, fpold.
  4) Return values:
     - _generate_knots_impl yields multiple t's and eventually returns None.
     - `_add_knots` returns:
         * (t_new, nplus) after inserting knots,
         * (not_a_knot_t, nplus) if n >= nmax,
         * (t, nplus) if n >= nest (storage cap).
)	TOLr   intrM   r   rL   r   r   r   )r)   rW   r   r   r   r   r   r5   fpold	residualsnplusaccnr   deltanpl1js   &&&&&&&&&&&      r1   
_add_knotsr     s    Z c'C	A6D 	y
,1Ks5<%'(U1WE!GSax34
 5\Q1( GGAJ 9 q$e++ 98O' * 8Or3   r:   r;   r   r   nestxnestyr   c               0   \        WW)4      w  rp pV
P                  V^,           8  g   VP                  V^,           8  dA   \        RV RV RV^,            RV^,            RV
P                   RVP                   R24      h\        V	^ ,          f
   V
^ ,          MV	^ ,          4      p\        V	^,          f
   V
R,          MV	^,          4      p\        V	^,          f
   V^ ,          MV	^,          4      p\        V	^,          f
   VR,          MV	^,          4      pRpVR8X  de   Vf   Ve   \        R	4      h\	        W4      p\	        W4      p\        WVVVW44      w  ppp\        VVVVVVWVW4      w  ppp\        VVVV3W434      # \        V
P                  WW7R
7      w  pppp\        VP                  VVWHR
7      w  ppppRpRp\        V 4      \        V4      ,           p Rp!Rp"Rp#\        V 4       F  p\        WVVVW44      w  ppp\        VVVVVVV
VVVV4      w  ppp$\        V4      V8X  d   \        V4      V8X  d   Tp!VV8  d    MVR8X  d9   \        WVVVVVVV\        P                  ! V$^,          ^R7      V"R7      w  pp"RpM7\        WVVVVVVV\        P                  ! V$^,          ^ R7      V#R7      w  pp#Rp\        V4      V8  d   \        V4      V8  d    MTpK  	  \        V4      V8X  d"   \        V4      V8X  d   \        XVVX3W434      # ^p\        VV4      w  p%p&p'\        VV4      w  p(p)p*\        V%V&V'4      p%\        V(V)V*4      p(\        WVVVW44      w  ppp\!        VV%VV(VVVWVWVV!VVR7      w  pp+p,\        V,VVV+3W434      # )a  
Core adaptive bivariate spline fitter using the 1/p-penalty convention.

Parameters
----------
x, y : array_like
    Strictly increasing coordinate vectors.
Z : array_like, shape (len(x), len(y))
    Data grid.
kx, ky : int, optional
    Spline degrees along x and y, default 3 (cubic).
s : float, optional
    Target residual (`fp` target). `s = 0` requests an interpolatory
    surface; `s > 0` triggers smoothing with penalty weight **1/p**.
maxit : int, optional
    Maximum iterations for the `p`-search when smoothing, default 50.
nestx, nesty : int or None
    Max coefficient counts per axis (nesting limits).
bbox : sequence of 4 scalars
    Optional domain limits `(xb, xe, yb, ye)`. Use `None` entries to skip.

Returns
-------
NdBSpline
    Fitted 2-D spline surface.

Notes
-----
The internal smoothing parameter `p` follows the **inverse**-penalty
rule: penalty term is 1/p.  Hence, larger `p` -> weaker smoothing
(approaching interpolation), while smaller `p` -> stronger smoothing.
A sentinel value `p == -1` is interpreted as *p = inf*, corresponding to
an exact (interpolatory) fit.

The iterative process adaptively grows knot vectors based on residual
energy and optionally performs a 1-D search over `p` to satisfy `fp ~ s`.
z/Not enough samples inside bbox for degrees (kx=z, ky=z ). Need at least k+1 per axis: (z, z). Got (z).Nr   zs == 0 is interpolation only)r   r*   r   )r   r   r   r5   r   r   r   r)   )r   r   r   )r   r   r   rk   r   r   r   r<   r   r   rL   r   r   rz   r   r>   r   )-r)   r*   r   r:   r;   r   r   r   r   r   x_fity_fitZ_fitr   r   r   r   r   ro   r   r   r|   r}   r~   C0r5   nminxnmaxxnminynmaxyr   	last_axismpmr   nplusxnplusyr   Drx	offset_dxnc_dxDry	offset_dync_dyC_smfp_sms-   &&&$$$$$$$                                   r1   _regrid_fitpackr     s   R !1q ?E%AzzR!V

b1f 5=bTrd K,,.qD6BqD6 :JJ<r%**R1
 	
 
47?uQxQ	8B	DGOuRya	9B	47?uQxQ	8B	DGOuRya	9B
ACx 1;<< ##,1b"b.R%b"a%'U%*3	B  RRL2(;;/

BBSBue/

BBSBueEI
a&3q6/C
CFF 3Z,1b"b.R
 &b"a&("e&("e&+-	B r7eB5 0C6 #1bu5r&&AA.	JB
 I#1bu5r&&AA.	JB
 I r7eB5 0S V 2w%CGu,RRL2(;;	A RLCE RLCE
sIu
-C
sIu
-C(aR)KRQ$Rb#q%'U%'q%(aANAtU EBD>B8<<r3   c               N   Vf   R.^,          p\         P                  ! V \        R7      p \         P                  ! V\        R7      p\         P                  ! V\        R7      p\         P                  ! V4      p\        V4      p\         P                  ! \         P
                  ! V 4      R8  4      '       g   \        R4      h\         P                  ! \         P
                  ! V4      R8  4      '       g   \        R4      hV P                  VP                  ^ ,          8w  d   \        R4      hVP                  VP                  ^,          8w  d   \        R4      hVR8  d   \        R4      hVP                  R8X  g   \        R	VP                   24      h\        WW$WVVRRVR
7
      # )ab  
Interface for 2-D smoothing B-spline fitting (1/p penalty form).

Parameters
----------
x, y : array_like
    Strictly increasing 1-D coordinate vectors.
z : array_like, shape (len(x), len(y))
    Data grid.
bbox : sequence of 4 scalars
    Optional bounding box ``(xb, xe, yb, ye)``; use ``None`` entries to disable.
kx, ky : int, optional
    Spline degrees along `x` and `y`, default cubic (3).
s : float, optional
    Target smoothing residual (`fp` target). Must satisfy ``s >= 0``.
    The underlying formulation uses a **1/p** penalty, meaning:
    - small `p` -> heavy smoothing,
    - large `p` -> light smoothing (approaching interpolation).
    Setting `p == -1` internally denotes *p = inf*, i.e. a pure interpolant.
maxit : int, optional
    Maximum iterations for `p`-search if invoked.

Returns
-------
NdBSpline
    Fitted bivariate spline surface.
Nr   r   zx must be strictly increasingzy must be strictly increasingz7x dimension of z must have same number of elements as xz7y dimension of z must have same number of elements as yzs should be s >= 0.0z"bbox shape should be (4,), found: )r:   r;   r   r   r   r   r   )   )
r   r   rk   r&   r!   r"   r   r   r   r   )r)   r*   r   r   r:   r;   r   r   s   &&&$$$$$r1   _regridr   !  s=   8 |vax


1E"A


1E"A


1E"A88D>DaA66"''!*s"##89966"''!*s"##899vvRSSvvRSS	C/00::=djj\JKK	a2%$T+ +r3   )rH   rH   T)NNr@   )rc   numpyr   scipy.interpolate._ndbspliner    scipy.interpolate._fitpack_repror   r   r   r    r   scipy.sparser   scipy._lib._utilr	   r2   r<   r>   rr   r   r   r   r   r   r   r   r   r   r   r   r3   r1   <module>r      s  Tj  2, ,  " *G0T3<) )X&PJX? ?DF# F# F# %'F#R%AP0f tnK=K=K=!K=
K=K= $K= 
K=\4+T 4+a 4+A 4+ 4+B 4+r3   