
    >qj                         d Z ddlZddlmZ ddlmZmZmZm	Z	 ddl
mZ ddlmZ ddlmZ d!d	Zd
 Z G d d      Zd Z	 d"dZ G d d      ZdddddZd Zd Zd#dZdZd ZdddddddddZddddddd Zy)$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                 j   t        | j                        dk7  rt        d      t        |d      }t        |d      }|dk  s|dk  rt        d      | j                  j
                  dd }t        j                  |      }t        j                  |      }|r;|j                  dk(  s|j                  dk(  rFt        j                  |j                  |j                  f|z   | j                  j                        }|S |j                  dk\  r6t        j                  t        j                  |      d	k\        st        d
      |j                  dk\  r6t        j                  t        j                  |      d	k\        st        d      t        j                  ||d      \  }}	t        j                  ||	fd      }
 | |
||f| j                        }|S |j
                  |j
                  k7  rt        j                   ||      \  }}|j                  dk(  r8t        j                  |j
                  |z   | j                  j                        S t        j                  |j#                         |j#                         fd      }
 | |
||f| j                        }|j%                  |j
                  |z         S )aC  
    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dyr   z,order of derivative must be positive or zeroN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              X/opt/rentech/trading_bot/.venv/lib/python3.12/site-packages/scipy/interpolate/_regrid.py_ndbspline_call_like_bivariater4      s   F 466{aEFF	r4	 B	r4	 B	AvaGHHvv||ABH


1A


1A66Q;!&&A+88QVVQVV,x7tvv||LDKFFaK"&&s):";PQQFFaK"&&s):";PQQ{{1a$/1XXq!f2&BB81A1AB77agg&&q!,DAq66Q;88AGGh.dffllCCXXqwwy!''),26BB81A1AB||AGGh.//    c                     t        |d         t        |d         }}|\  }}|d   j                  ||z
  dz
  ||z
  dz
        }t        |d   |d   f||      S )a  
    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   )r   r)   r   )fptckdegreesnxnykxkyr   s           r3   return_NdBSpliner>      sf    0 Q[#c!f+BFBArBw{BGaK0Ac!fc!f%q'22r5   c                   2    e Zd ZdZd Zed        Zd Zd Zy)PackedMatrixas  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                 .    || _         || _        || _        y N)aoffsetnc)selfrC   rD   rE   s       r3   __init__zPackedMatrix.__init__  s    r5   c                 L    | j                   j                  d   | j                  fS )Nr   )rC   r   rE   )rF   s    r3   r   zPackedMatrix.shape  s    vv||A''r5   c                 j   t        j                  | j                        }| j                  j                  d   }t	        |j                  d         D ]_  }t        | j                  | j                  |   z
  |      }| j                  |d |f   ||| j                  |   | j                  |   |z   f<   a |S )Nr   r   )r   r"   r   rC   rangeminrE   rD   )rF   outneleminels        r3   todensezPackedMatrix.todense  s    hhtzz"Qsyy|$ 	JAdggA.6C:>&&DSD/C4;;q>$++a.3"6667	J 
r5   c                    | j                   j                         }t        j                  | j                  |dz         j                  d|dz         }|t        j                  |dz   | j                  j                        z   }|j                         }t        j                  d|dz   |dz   z  |dz   | j                  j                        }t        |||f|||z
  dz
  f      S )Nr   r   r   r   )r   )	rC   r(   r   repeatrD   r)   aranger   r
   )rF   kmlen_tdataindicesindptrs          r3   tocsrzPackedMatrix.tocsr   s    vv||~ ))DKK1-55b!A#>BIIac1B1BCC--/1q1uQ/Q!%!2!24 7F#eai!m$ 	r5   N)	__name__
__module____qualname____doc__rG   propertyr   rP   rZ    r5   r3   r@   r@     s*    
 ( (r5   r@   c                    |dk(  r5| j                   j                         | j                  j                         |fS |dz   }t        j                  ||j
                  d   z   |dz   ft              }| j                   d|ddf   |d|d|f<   |j                   |z  ||dddf<   t        j                  | j                  |j                  f      }|||fS )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   r   r   r   r   N)rC   copyrD   r   r"   r   floatconcatenate)ADrE   rT   pnzAArD   s           r3   _stack_augmented_fitpackrj   2  s    < 	Bwssxxz188==?B..	
QB	2
?AE*%	8B33ssAv;BssCRCxLqBrsAvJ^^QXXqxx01Fvr>r5   c                    t        j                  |      }t        j                  |	      }t        | || j                  d   ||      \  }}}| j                  }t        |||j                  d   ||      \  }}}|j                  }|dk7  rLt        j
                  |t        j                  |j                  d   |j                  d   ft              g      }t        j                  ||||       t        j                  ||||||||d	      \  }}}t        j                  |j                        }|dk7  rLt        j
                  |t        j                  |j                  d   |j                  d   ft              g      }t        j                  ||||       t        j                  |||	|||||d	      \  }}}| j                  ||j                  d   t        |            }|j                  ||	j                  d   t        |            }||j                  z  |j                  z  }|
|z
  }t        j                  t        j                   |            }|j                  ||fS )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   r   r   r   F)r   	ones_likerj   r   rE   vstackr"   rc   r	   	qr_reducefpbackascontiguousarrayTrZ   r   sumsquare)AxAyQrg   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_yrq   _Cr7   _Ax_AyzhatRs                                  r3   _solve_2d_fitpackr   Z  s   R ,,s
C
,,s
C %=
BR%$!FL'55D %=
BR%$!FL'55D 	Bw IIq"((BHHQK#<EJKL v|Wa8 ooc	2r3	5GAq! 	QSS!A 	BwIIq"((BHHQK#<EJKL v|Wa8 QB		HAq" ((2syy|SW
-C
((2syy|SW
-C 9suuD
 	
DA			!	B 33A:r5   c                       e Zd ZdZd Zd Zy)Fa  
    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                     || _         || _        || _        || _        || _        || _        || _        || _        |	| _        |
| _	        || _
        || _        y rB   )rt   r|   ru   r}   rv   r<   rw   rx   r=   ry   rz   r{   )rF   rt   r|   ru   r}   rv   r<   rw   rx   r=   ry   rz   r{   s                r3   rG   z
F.__init__I  sX     r5   c                 f   t        | j                  | j                  | j                  j	                         || j
                  | j                  | j                  | j                  | j                  | j                  | j                  | j                  | j                        \  }}}|| _        || _        |S )N)r|   r}   )r   rt   ru   rv   rb   r<   rw   rx   r=   ry   rz   r{   r|   r}   r   r7   )rF   rg   r   r7   r   s        r3   __call__z
F.__call__Y  s}    
 %GGTWWdffkkmtwwGGTWWdhhww477	$2q
 	r5   N)r[   r\   r]   r^   rG   r   r`   r5   r3   r   r   &  s     D r5   r   g      ?gMbP?(   )p_inittol_relmaxitc                   t        | |||||||||	|
|      fd} |d      }d|z
  ft        j                  |ff}t        |z  d      }t	        |||||      }|j
                  } |      }j                  }|||fS )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                      |       z
  S rB   r`   )rg   fp_atss    r3   gz_p_search_hit_s.<locals>.g  s    Qx!|r5   r   r   g-q=)r   )r   r   infmaxr   rootr   )rt   r|   ru   r}   rv   r<   rw   rx   r=   ry   rz   r{   r   fp0r   r   r   r   fpmsbracketftolrp_starfp_starC_starr   s               `            @r3   _p_search_hit_sr   h  s    l b"b"a#r2sA'E R5DS1W~~.Gq7{E"D!VWd%8AVVFFmGWWF67""r5   c                    t        |D cg c]  }|du  c}      r| ||t        d      t        d      fS |\  }}}}||k  r||k  st        d      t        j                  | |k\  | |k  z        d   }	t        j                  ||k\  ||k  z        d   }
|	j
                  dk(  s|
j
                  dk(  rt        d      | |	   ||
   |t        j                  |	|
         t        j                  |	   t        j                  |
   fS c c}w )al  
    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 < yer   z$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              r3   _apply_bbox_gridr     s    2 t,eETM,-!QdU4[00NBBGR@AA	17qBw'	(	+B	17qBw'	(	+B	ww!|rww!|?@@R5!B%266"b>*BEE"IruuRy@@ -s   Dc                    t        j                  |       }t        j                  |      }t        j                  | |||      \  }	}
}t        j                  ||||      \  }}}|j	                         }t        |	|
|      t        |||      |fS rB   )r   rl   r	   data_matrixrb   r@   )r+   r,   r{   rw   ry   r<   r=   r~   r   rt   offset_xr   ru   offset_yr   rv   s                   r3   _build_design_matricesr     s    
,,q/C
,,q/C!--aR=B$!--aR=B$	AXt,Xt, r5   c                     |t        | |z   dz   d|z  dz         }n#|d|dz   z  k  rt        d|dd|dz   z   d      d|dz   z  }| |z   dz   }t        j                  |g|dz   z  |g|dz   z  z         }||||fS )ap  
    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)
    r   r      z`nest` too small: nest = z < 2*(k+1) = .)r   r   r   r    )rU   r   r   rT   nestnminnmaxr   s           r3   _initialise_knotsr     s    H |1q519acAg&!QU)9$-1Q3yPQRSSa!e9Dq519D 	

B41:ac
*+AdD$r5   c                 b   |t         z  }|j                  }||z
  }||k(  rd}
n=||z
  }||kD  rt        |
|z  |z        n|
dz  }t        |
dz  t	        ||
dz  d            }
t        |
      D ]?  }t        | |||	      }|j                  d   }||k\  rt        | |      |
fc S ||k\  s;||
fc S  ||
fS )aQ  
    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).
    r   r   r   )	TOLr!   intrK   r   rJ   r   r   r   )r+   rT   r   r   r   r   r   r7   fpold	residualsnplusaccnr   deltanpl1js                    r3   
_add_knotsr     s    Z c'C	A6D 	Dy
,1CKs54<%'(U1WE!GSuax34
 5\ Q1i( GGAJ 9 q!$e++ 9e8O'* e8Or5   r   r   2   r<   r=   r   r   nestxnestyr   c                z   t        | |||	      \  }
}}}}|
j                  |dz   k  s|j                  |dz   k  r8t        d| d| d|dz    d|dz    d|
j                   d|j                   d      t        |	d   |
d   n|	d         }t        |	d   |
d
   n|	d         }t        |	d   |d   n|	d         }t        |	d   |d
   n|	d         }d
}|dk(  rg||t        d      t	        |
|      }t	        ||      }t        |
||||||      \  }}}t        |||||||
||||      \  }}}t        ||||f||f      S t        |
j                  ||||      \  }}}}t        |j                  ||||      \  }}}}d	}d}t        |       t        |      z   } d	}!d	}"d	}#t        |       D ]  }t        |
||||||      \  }}}t        |||||||
||||      \  }}}$t        |      |k(  rt        |      |k(  r|}!||k  r n|dk(  r4t        |
||||||||t        j                  |$dz  d      |"      \  }}"d}n3t        |||||||||t        j                  |$dz  d      |#      \  }}#d}t        |      |k\  rt        |      |k\  r n|} t        |      |k(  r t        |      |k(  rt        ||f||f      S d}t        ||      \  }%}&}'t        ||      \  }(})}*t        |%|&|'      }%t        |(|)|*      }(t        |
||||||      \  }}}t!        ||%||(||||
||||||!||      \  }}+},t        |,|||+f||f      S )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`.
    r   z/Not enough samples inside bbox for degrees (kx=z, ky=z ). Need at least k+1 per axis: (z, z). Got (z).r   Nr   r   r   r   zs == 0 is interpolation only)r   r,   r   )r   r   r   r7   r   r   r   r+   )r   r   )r   r!   r   rc   r   r   r   r>   r   r   rJ   r   r   rr   r   r@   r   )-r+   r,   r   r<   r=   r   r   r   r   r   x_fity_fitZ_fitr   r   r   r   r   rg   rw   ry   rt   ru   rv   C0r7   nminxnmaxxnminynmaxyr   	last_axismpmr   nplusxnplusyr   Drx	offset_dxnc_dxDry	offset_dync_dyC_smfp_sms-                                                r3   _regrid_fitpackr     s   R !1Aq$ ?E5%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;<< ##,E1b"b".R%b"a%'UB%*E3	B  RRL2r(;;/

BBUSBue/

BBUSBueEI
a&3q6/C
CFF 3Z ),E1b"b".R
 &b"a&("e&("e&+-	B r7eB5 0C6 #r1bu5r&&AA.	JB
 I#r1bu5r&&AA.	JB
 I r7eB5 0S)V 2w%CGu,RRL2r(;;	A RLCE RLCE
sIu
-C
sIu
-C(uaRR)KRQ$Rb#q%'UB%'q%(aANAtU EBD>B8<<r5   )r   r<   r=   r   r   c                    |dgdz  }t        j                  | t              } t        j                  |t              }t        j                  |t              }t        j                  |      }t        |      }t        j                  t        j
                  |       dkD        st        d      t        j                  t        j
                  |      dkD        st        d      | j                  |j                  d   k7  rt        d      |j                  |j                  d	   k7  rt        d
      |dk  rt        d      |j                  dk(  st        d|j                         t        | ||||||dd|
      S )a  
    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.
    N   r   r   zx must be strictly increasingzy must be strictly increasingr   z7x dimension of z must have same number of elements as xr   z7y dimension of z must have same number of elements as yzs should be s >= 0.0)r   z"bbox shape should be (4,), found: r   )
r   r    rc   r(   r#   r$   r   r!   r   r   )r+   r,   r{   r   r<   r=   r   r   s           r3   _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	1aB2%$T+ +r5   )r   r   T)NNrB   )r^   numpyr   scipy.interpolate._ndbspliner    scipy.interpolate._fitpack_repror   r   r   r    r	   scipy.sparser
   scipy._lib._utilr   r4   r>   r@   rj   r   r   r   r   r   r   r   r   r   r   r`   r5   r3   <module>r      s   Tj  2, ,  " *G0T3<) )X&V #'JX? ?J BF#R%AP0f tp c
D	K=\ "aAB 4+r5   