o
    Eb3                     @   s$  d dl Z d dlZd dlmZ d dlmZmZ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lmZ d d	lmZ d d
lmZ d dlmZ g dZdd Zd.ddZdd Zdd ZG dd dZdd Zdd Z dd Z!dd Z"d d! Z#d"d# Z$d$d% Z%d&d' Z&	 	)d/d*d+Z'd0d,d-Z(dS )1    N)normalize_axis_index)get_lapack_funcsLinAlgErrorcholesky_bandedcho_solve_bandedsolvesolve_banded   )_bspl)_fitpack_impl)_fitpack)prod)poch)	csr_array)combinations)BSplinemake_interp_splinemake_lsq_splinec                 C   s   t | t jr
t jS t jS )z>Return np.complex128 for complex dtypes, np.float64 otherwise.)npZ
issubdtypeZcomplexfloatingZcomplex_float_dtype r   =/usr/lib/python3/dist-packages/scipy/interpolate/_bsplines.py
_get_dtype   s   r   Fc                 C   s@   t | } t| j}| j|dd} |rt |  std| S )zConvert the input into a C contiguous float array.

    NB: Upcasts half- and single-precision floats to double precision.
    F)copyz$Array must not contain infs or nans.)r   ascontiguousarrayr   r   Zastypeisfiniteall
ValueError)xcheck_finiteZdtypr   r   r   _as_float_array   s   

r"   c                    s2   |dkrdS t  fddtd|d D S )z
    Dual polynomial of the B-spline B_{j,k,t} -
    polynomial which is associated with B_{j,k,t}:
    $p_{j,k}(y) = (y - t_{j+1})(y - t_{j+2})...(y - t_{j+k})$
    r   r	   c                    s   g | ]
} |   qS r   r   ).0ijtyr   r   
<listcomp>0   s    z_dual_poly.<locals>.<listcomp>)r   r   range)r&   kr'   r(   r   r%   r   
_dual_poly(   s   &r,   c              
      s   dkrt |S |krtd|S tttd | d  d}tt t d  D ]|t fddtd|d D 7 }q2|S )z=
    d-th derivative of the dual polynomial $p_{j,k}(y)$
    r   r	   c                    s0   g | ]}|    vr|   qS r   r   )r#   pZcombdr$   r&   r'   r(   r   r   r)   >   s    z#_diff_dual_poly.<locals>.<listcomp>)r,   r   listr   r*   lenr   r   )r&   r+   r(   r/   r'   resr   r.   r   _diff_dual_poly3   s   
 2r3   c                       s   e Zd ZdZd fdd	ZedddZedd	 Zed d
dZ	edd Z
d!ddZdd Zdd Zd"ddZd"ddZd#ddZed$ddZ  ZS )%r   a  Univariate spline in the B-spline basis.

    .. math::

        S(x) = \sum_{j=0}^{n-1} c_j  B_{j, k; t}(x)

    where :math:`B_{j, k; t}` are B-spline basis functions of degree `k`
    and knots `t`.

    Parameters
    ----------
    t : ndarray, shape (n+k+1,)
        knots
    c : ndarray, shape (>=n, ...)
        spline coefficients
    k : int
        B-spline degree
    extrapolate : bool or 'periodic', optional
        whether to extrapolate beyond the base interval, ``t[k] .. t[n]``,
        or to return nans.
        If True, extrapolates the first and last polynomial pieces of b-spline
        functions active on the base interval.
        If 'periodic', periodic extrapolation is used.
        Default is True.
    axis : int, optional
        Interpolation axis. Default is zero.

    Attributes
    ----------
    t : ndarray
        knot vector
    c : ndarray
        spline coefficients
    k : int
        spline degree
    extrapolate : bool
        If True, extrapolates the first and last polynomial pieces of b-spline
        functions active on the base interval.
    axis : int
        Interpolation axis.
    tck : tuple
        A read-only equivalent of ``(self.t, self.c, self.k)``

    Methods
    -------
    __call__
    basis_element
    derivative
    antiderivative
    integrate
    construct_fast
    design_matrix
    from_power_basis

    Notes
    -----
    B-spline basis elements are defined via

    .. math::

        B_{i, 0}(x) = 1, \textrm{if $t_i \le x < t_{i+1}$, otherwise $0$,}

        B_{i, k}(x) = \frac{x - t_i}{t_{i+k} - t_i} B_{i, k-1}(x)
                 + \frac{t_{i+k+1} - x}{t_{i+k+1} - t_{i+1}} B_{i+1, k-1}(x)

    **Implementation details**

    - At least ``k+1`` coefficients are required for a spline of degree `k`,
      so that ``n >= k+1``. Additional coefficients, ``c[j]`` with
      ``j > n``, are ignored.

    - B-spline basis elements of degree `k` form a partition of unity on the
      *base interval*, ``t[k] <= x <= t[n]``.


    Examples
    --------

    Translating the recursive definition of B-splines into Python code, we have:

    >>> def B(x, k, i, t):
    ...    if k == 0:
    ...       return 1.0 if t[i] <= x < t[i+1] else 0.0
    ...    if t[i+k] == t[i]:
    ...       c1 = 0.0
    ...    else:
    ...       c1 = (x - t[i])/(t[i+k] - t[i]) * B(x, k-1, i, t)
    ...    if t[i+k+1] == t[i+1]:
    ...       c2 = 0.0
    ...    else:
    ...       c2 = (t[i+k+1] - x)/(t[i+k+1] - t[i+1]) * B(x, k-1, i+1, t)
    ...    return c1 + c2

    >>> def bspline(x, t, c, k):
    ...    n = len(t) - k - 1
    ...    assert (n >= k+1) and (len(c) >= n)
    ...    return sum(c[i] * B(x, k, i, t) for i in range(n))

    Note that this is an inefficient (if straightforward) way to
    evaluate B-splines --- this spline class does it in an equivalent,
    but much more efficient way.

    Here we construct a quadratic spline function on the base interval
    ``2 <= x <= 4`` and compare with the naive way of evaluating the spline:

    >>> from scipy.interpolate import BSpline
    >>> k = 2
    >>> t = [0, 1, 2, 3, 4, 5, 6]
    >>> c = [-1, 2, 0, -1]
    >>> spl = BSpline(t, c, k)
    >>> spl(2.5)
    array(1.375)
    >>> bspline(2.5, t, c, k)
    1.375

    Note that outside of the base interval results differ. This is because
    `BSpline` extrapolates the first and last polynomial pieces of B-spline
    functions active on the base interval.

    >>> import matplotlib.pyplot as plt
    >>> fig, ax = plt.subplots()
    >>> xx = np.linspace(1.5, 4.5, 50)
    >>> ax.plot(xx, [bspline(x, t, c ,k) for x in xx], 'r-', lw=3, label='naive')
    >>> ax.plot(xx, spl(xx), 'b-', lw=4, alpha=0.7, label='BSpline')
    >>> ax.grid(True)
    >>> ax.legend(loc='best')
    >>> plt.show()


    References
    ----------
    .. [1] Tom Lyche and Knut Morken, Spline methods,
        http://www.uio.no/studier/emner/matnat/ifi/INF-MAT5340/v05/undervisningsmateriale/
    .. [2] Carl de Boor, A practical guide to splines, Springer, 2001.

    Tr   c                    s  t    t|| _t|| _tj|tj	d| _
|dkr"|| _nt|| _| j
jd | j d }t|| jj}|| _|dkrIt| j|d| _|dk rQtd| j
jdkr[td|| jd k rntdd| d |f t| j
dk  r|td	tt| j
||d  dk rtd
t| j
 std| jjdk rtd| jjd |k rtdt| jj}tj| j|d| _d S )Nr   periodicr   r	   z Spline order cannot be negative.z$Knot vector must be one-dimensional.z$Need at least %d knots for degree %d   z(Knots must be in a non-decreasing order.z!Need at least two internal knots.z#Knots should not have nans or infs.z,Coefficients must be at least 1-dimensional.z0Knots, coefficients and degree are inconsistent.)super__init__operatorindexr+   r   asarraycr   Zfloat64r'   extrapolateboolshaper   ndimaxismoveaxisr   diffanyr1   uniquer   r   r   r   )selfr'   r;   r+   r<   r@   nZdt	__class__r   r   r7      s@   

 zBSpline.__init__c                 C   s0   t | }||||_|_|_||_||_|S )zConstruct a spline without making checks.

        Accepts same parameters as the regular constructor. Input arrays
        `t` and `c` must of correct shape and dtype.
        )object__new__r'   r;   r+   r<   r@   )clsr'   r;   r+   r<   r@   rE   r   r   r   construct_fast   s
   
zBSpline.construct_fastc                 C   s   | j | j| jfS )z@Equivalent to ``(self.t, self.c, self.k)`` (read-only).
        )r'   r;   r+   rE   r   r   r   tck  s   zBSpline.tckc                 C   sb   t |d }t|}tj|d d f| ||d d f| f }t|}d||< | ||||S )a]  Return a B-spline basis element ``B(x | t[0], ..., t[k+1])``.

        Parameters
        ----------
        t : ndarray, shape (k+2,)
            internal knots
        extrapolate : bool or 'periodic', optional
            whether to extrapolate beyond the base interval, ``t[0] .. t[k+1]``,
            or to return nans.
            If 'periodic', periodic extrapolation is used.
            Default is True.

        Returns
        -------
        basis_element : callable
            A callable representing a B-spline basis element for the knot
            vector `t`.

        Notes
        -----
        The degree of the B-spline, `k`, is inferred from the length of `t` as
        ``len(t)-2``. The knot vector is constructed by appending and prepending
        ``k+1`` elements to internal knots `t`.

        Examples
        --------

        Construct a cubic B-spline:

        >>> from scipy.interpolate import BSpline
        >>> b = BSpline.basis_element([0, 1, 2, 3, 4])
        >>> k = b.k
        >>> b.t[k:-k]
        array([ 0.,  1.,  2.,  3.,  4.])
        >>> k
        3

        Construct a quadratic B-spline on ``[0, 1, 1, 2]``, and compare
        to its explicit form:

        >>> t = [-1, 0, 1, 1, 2]
        >>> b = BSpline.basis_element(t[1:])
        >>> def f(x):
        ...     return np.where(x < 1, x*x, (2. - x)**2)

        >>> import matplotlib.pyplot as plt
        >>> fig, ax = plt.subplots()
        >>> x = np.linspace(0, 2, 51)
        >>> ax.plot(x, b(x), 'g', lw=3)
        >>> ax.plot(x, f(x), 'r', lw=8, alpha=0.4)
        >>> ax.grid(True)
        >>> plt.show()

        r5   r   r	   g      ?)r1   r"   r   r_
zeros_likerL   )rK   r'   r<   r+   r;   r   r   r   basis_element  s   8,
zBSpline.basis_elementc                 C   s   t |d}t |d}|jdkst|dd |dd k r&td| dt|d| d k r8td| dt||| k sOt|||jd	 | d  krWtd
| d|jd	 |jd	 }}t	
|||\}}t||f||| d fS )a  
        Returns a design matrix as a CSR format sparse array.

        Parameters
        ----------
        x : array_like, shape (n,)
            Points to evaluate the spline at.
        t : array_like, shape (nt,)
            Sorted 1D array of knots.
        k : int
            B-spline degree.

        Returns
        -------
        design_matrix : `csr_array` object
            Sparse matrix in CSR format where in each row all the basis
            elements are evaluated at the certain point (first row - x[0],
            ..., last row - x[-1]).

        Examples
        --------
        Construct a design matrix for a B-spline

        >>> from scipy.interpolate import make_interp_spline, BSpline
        >>> x = np.linspace(0, np.pi * 2, 4)
        >>> y = np.sin(x)
        >>> k = 3
        >>> bspl = make_interp_spline(x, y, k=k)
        >>> design_matrix = bspl.design_matrix(x, bspl.t, k)
        >>> design_matrix.toarray()
        [[1.        , 0.        , 0.        , 0.        ],
        [0.2962963 , 0.44444444, 0.22222222, 0.03703704],
        [0.03703704, 0.22222222, 0.44444444, 0.2962963 ],
        [0.        , 0.        , 0.        , 1.        ]]

        Construct a design matrix for some vector of knots

        >>> k = 2
        >>> t = [-1, 0, 1, 2, 3, 4, 5, 6]
        >>> x = [1, 2, 3, 4]
        >>> design_matrix = BSpline.design_matrix(x, t, k).toarray()
        >>> design_matrix
        [[0.5, 0.5, 0. , 0. , 0. ],
        [0. , 0.5, 0.5, 0. , 0. ],
        [0. , 0. , 0.5, 0.5, 0. ],
        [0. , 0. , 0. , 0.5, 0.5]]

        This result is equivalent to the one created in the sparse format

        >>> c = np.eye(len(t) - k - 1)
        >>> design_matrix_gh = BSpline(t, c, k)(x)
        >>> np.allclose(design_matrix, design_matrix_gh, atol=1e-14)
        True

        Notes
        -----
        .. versionadded:: 1.8.0

        In each row of the design matrix all the basis elements are evaluated
        at the certain point (first row - x[0], ..., last row - x[-1]).

        `nt` is a lenght of the vector of knots: as far as there are
        `nt - k - 1` basis elements, `nt` should be not less than `2 * k + 2`
        to have at least `k + 1` basis element.

        Out of bounds `x` raises a ValueError.
        Tr	   NrO   z2Expect t to be a 1-D sorted array_like, but got t=.r5   zLength t is not enough for k=r   zOut of bounds w/ x = )r"   r?   r   rC   r   r1   minmaxr>   r
   Z_make_design_matrixr   )rK   r    r'   r+   rF   ntdataidxr   r   r   design_matrixM  s   
E
(
.zBSpline.design_matrixNc           	      C   s<  |du r| j }t|}|j|j}}tj| tjd}|dkrE| jj	| j
 d }| j| j
 || j| j
  | j| | j| j
    }d}tjt|t| jjdd f| jjd}|   | |||| ||| jjdd  }| jdkrtt|j}|||| j  |d|  ||| j d  }||}|S )a  
        Evaluate a spline function.

        Parameters
        ----------
        x : array_like
            points to evaluate the spline at.
        nu: int, optional
            derivative to evaluate (default is 0).
        extrapolate : bool or 'periodic', optional
            whether to extrapolate based on the first and last intervals
            or return nans. If 'periodic', periodic extrapolation is used.
            Default is `self.extrapolate`.

        Returns
        -------
        y : array_like
            Shape is determined by replacing the interpolation axis
            in the coefficient array with the shape of `x`.

        Nr   r4   r	   Fr   )r<   r   r:   r>   r?   r   Zravelr   r'   sizer+   emptyr1   r   r;   r   _ensure_c_contiguous	_evaluatereshaper@   r0   r*   Z	transpose)	rE   r    nur<   Zx_shapeZx_ndimrF   outlr   r   r   __call__  s(   
 
*
0
zBSpline.__call__c              	   C   s0   t | j| j| jjd d| j|||| d S )Nr   rO   )r
   evaluate_spliner'   r;   r^   r>   r+   )rE   Zxpr_   r<   r`   r   r   r   r]     s   zBSpline._evaluatec                 C   s4   | j jjs| j  | _ | jjjs| j | _dS dS )zs
        c and t may be modified by the user. The Cython code expects
        that they are C contiguous.

        N)r'   flagsc_contiguousr   r;   rM   r   r   r   r\     s
   

zBSpline._ensure_c_contiguousr	   c                 C   sp   | j }t| jt| }|dkr"tj|t|f|jdd  f }t| j|| j	f|}| j
|| j| jdS )ad  Return a B-spline representing the derivative.

        Parameters
        ----------
        nu : int, optional
            Derivative order.
            Default is 1.

        Returns
        -------
        b : BSpline object
            A new instance representing the derivative.

        See Also
        --------
        splder, splantider

        r   r	   Nr<   r@   )r;   r1   r'   r   rP   zerosr>   r   Zsplderr+   rL   r<   r@   )rE   r_   r;   ctrN   r   r   r   
derivative  s   $
zBSpline.derivativec                 C   s   | j }t| jt| }|dkr"tj|t|f|jdd  f }t| j|| j	f|}| j
dkr5d}n| j
}| j||| jdS )a  Return a B-spline representing the antiderivative.

        Parameters
        ----------
        nu : int, optional
            Antiderivative order. Default is 1.

        Returns
        -------
        b : BSpline object
            A new instance representing the antiderivative.

        Notes
        -----
        If antiderivative is computed and ``self.extrapolate='periodic'``,
        it will be set to False for the returned instance. This is done because
        the antiderivative is no longer periodic and its correct evaluation
        outside of the initially given x interval is difficult.

        See Also
        --------
        splder, splantider

        r   r	   Nr4   Frf   )r;   r1   r'   r   rP   rg   r>   r   
splantiderr+   r<   rL   r@   )rE   r_   r;   rh   rN   r<   r   r   r   antiderivative   s   $
zBSpline.antiderivativec              	   C   sb  |du r| j }|   d}||k r||}}d}| jj| j d }|dkrS|sSt|| j| j }t|| j| }| jjdkrS| j	\}}}t
|||||\}	}
|	| S tjdt| jjdd f| jjd}| j}t| jt| }|dkrtj|t|f|jdd  f }t| j|| jfd\}}}|dkr| j| j | j| }}|| }|| }t||\}}|dkrtj||gtjd}t|||jd d||dd| |d |d  }	|	|9 }	ntjdt| jjdd f| jjd}	||| |  }|| }||kr.tj||gtjd}t|||jd d||dd| |	|d |d  7 }	nutj||gtjd}t|||jd d||dd| |	|d |d  7 }	tj||| | gtjd}t|||jd d||dd| |	|d |d  7 }	n$tj||gtjd}t|||jd d||d|| |d |d  }	|	|9 }	|	|jdd S )	a  Compute a definite integral of the spline.

        Parameters
        ----------
        a : float
            Lower limit of integration.
        b : float
            Upper limit of integration.
        extrapolate : bool or 'periodic', optional
            whether to extrapolate beyond the base interval,
            ``t[k] .. t[-k-1]``, or take the spline to be zero outside of the
            base interval. If 'periodic', periodic extrapolation is used.
            If None (default), use `self.extrapolate`.

        Returns
        -------
        I : array_like
            Definite integral of the spline over the interval ``[a, b]``.

        Examples
        --------
        Construct the linear spline ``x if x < 1 else 2 - x`` on the base
        interval :math:`[0, 2]`, and integrate it

        >>> from scipy.interpolate import BSpline
        >>> b = BSpline.basis_element([0, 1, 2])
        >>> b.integrate(0, 1)
        array(0.5)

        If the integration limits are outside of the base interval, the result
        is controlled by the `extrapolate` parameter

        >>> b.integrate(-1, 1)
        array(0.0)
        >>> b.integrate(-1, 1, extrapolate=False)
        array(0.5)

        >>> import matplotlib.pyplot as plt
        >>> fig, ax = plt.subplots()
        >>> ax.grid(True)
        >>> ax.axvline(0, c='r', lw=5, alpha=0.5)  # base interval
        >>> ax.axvline(2, c='r', lw=5, alpha=0.5)
        >>> xx = [-1, 1, 2]
        >>> ax.plot(xx, b(xx))
        >>> plt.show()

        Nr	   rO   r4   r5   r   r   F)r<   r\   r'   rZ   r+   rU   rT   r;   r?   rN   _dierckxZ_splintr   r[   r   r>   r   r1   rP   rg   r   rj   divmodr:   r   r
   rc   r^   )rE   abr<   signrF   r'   r;   r+   ZintegralZwrkr`   rh   tacakatsteZperiodintervalZ	n_periodsleftr    r   r   r   	integrate(  sz   0
&$







zBSpline.integrate
not-a-knotc                 C   s  ddl m} t||stdt| |j}|j}|jjd d }|jd }|dkr0t||}n|dks8|dkr>t	||}n|dkrHt
||}ntd	| |jd || d  }	tj||	 |jjd
}
t|d D ]g}t|d D ](}|
|  t|d | |||f  td||  t|||| || 7  < qrt|d ||	 D ],}|
|  t|d | |||d f  td||  t||||d  || 7  < qqj| ||
||j|jS )a  
        Construct a polynomial in the B-spline basis
        from a piecewise polynomial in the power basis.

        For now, accepts ``CubicSpline`` instances only.

        Parameters
        ----------
        pp : CubicSpline
            A piecewise polynomial in the power basis, as created
            by ``CubicSpline``
        bc_type : string, optional
            Boundary condition type as in ``CubicSpline``: one of the
            ``not-a-knot``, ``natural``, ``clamped``, or ``periodic``.
            Necessary for construction an instance of ``BSpline`` class.
            Default is ``not-a-knot``.

        Returns
        -------
        b : BSpline object
            A new instance representing the initial polynomial
            in the B-spline basis.

        Notes
        -----
        .. versionadded:: 1.8.0

        Accepts only ``CubicSpline`` instances for now.

        The algorithm follows from differentiation
        the Marsden's identity [1]: each of coefficients of spline
        interpolation function in the B-spline basis is computed as follows:

        .. math::

            c_j = \sum_{m=0}^{k} \frac{(k-m)!}{k!}
                       c_{m,i} (-1)^{k-m} D^m p_{j,k}(x_i)

        :math:`c_{m, i}` - a coefficient of CubicSpline,
        :math:`D^m p_{j, k}(x_i)` - an m-th defivative of a dual polynomial
        in :math:`x_i`.

        ``k`` always equals 3 for now.

        First ``n - 2`` coefficients are computed in :math:`x_i = x_j`, e.g.

        .. math::

            c_1 = \sum_{m=0}^{k} \frac{(k-1)!}{k!} c_{m,1} D^m p_{j,3}(x_1)

        Last ``nod + 2`` coefficients are computed in ``x[-2]``,
        ``nod`` - number of derivatives at the ends.

        For example, consider :math:`x = [0, 1, 2, 3, 4]`,
        :math:`y = [1, 1, 1, 1, 1]` and bc_type = ``natural``

        The coefficients of CubicSpline in the power basis:

        :math:`[[0, 0, 0, 0, 0], [0, 0, 0, 0, 0],
        [0, 0, 0, 0, 0], [1, 1, 1, 1, 1]]`

        The knot vector: :math:`t = [0, 0, 0, 0, 1, 2, 3, 4, 4, 4, 4]`

        In this case

        .. math::

            c_j = \frac{0!}{k!} c_{3, i} k! = c_{3, i} = 1,~j = 0, ..., 6

        References
        ----------
        .. [1] Tom Lyche and Knut Morken, Spline Methods, 2005, Section 3.1.2

        r	   )CubicSplinez=Only CubicSpline objects are acceptedfor now. Got %s instead.r   ry   naturalclampedr4   Unknown boundary condition: %sr   r5   rO   )Z_cubicrz   
isinstanceNotImplementedErrortyper    r;   r>   _not_a_knot_augknt_periodic_knots	TypeErrorr   rg   r   r*   r   Zpowerr3   rL   r<   r@   )rK   ppbc_typerz   r    Zcoefr+   rF   r'   Znodr;   mr$   r&   r   r   r   from_power_basis  sB   L

"
&
zBSpline.from_power_basis)Tr   )T)r   N)r	   N)ry   )__name__
__module____qualname____doc__r7   classmethodrL   propertyrN   rR   rY   rb   r]   r\   ri   rk   rx   r   __classcell__r   r   rG   r   r   C   s*     	/
>

X/


( r   c                 C   st   t | } |d dkrtd| |d d }| |d | d  }t j| d f|d  || d f|d  f }|S )zSGiven data x, construct the knot vector w/ not-a-knot BC.
    cf de Boor, XIII(12).r5   r	   z Odd degree for now only. Got %s.r   rO   )r   r:   r   rP   )r    r+   r   r'   r   r   r   r     s   
,r   c                 C   s$   t j| d f| | | d f| f S )zBConstruct a knot vector appropriate for the order-k interpolation.r   rO   )r   rP   )r    r+   r   r   r   r   *  s   $r   c                 C   sR   t | tr'| dkrdt|fg} | S | dkr!dt|fg} | S td|  | S )Nr|   r	   r{   r5   zUnknown boundary condition : %s)r~   strr   rg   r   )derivZtarget_shaper   r   r   _convert_string_aliases/  s   
r   c              
   C   sV   | d ur zt |  \}}W n ty } zd}t||d }~ww g g }}t||S )Nz^Derivatives, `bc_type`, should be specified as a pair of iterables of pairs of (order, value).)zipr   r   r   Z
atleast_1d)r   ZordsZvalsemsgr   r   r   _process_deriv_spec:  s   

r   c                 C   s  ||d  }t |d d |d d  }| jd d }t|d |f}t||d f}	||d|d|f< d|	t|t|| f< ||| d| df< d|	t|| t|f< t||f| |}
tt||	|
  t|}t||f| |}||
||	|    }|S )aK  
    Solve a cyclic banded linear system with upper right
    and lower blocks of size ``(k-1) / 2`` using
    the Woodbury formula
    
    Parameters
    ----------
    A : 2-D array, shape(k, n)
        Matrix of diagonals of original matrix(see 
        ``solve_banded`` documentation).
    ur : 2-D array, shape(bs, bs)
        Upper right block matrix.
    ll : 2-D array, shape(bs, bs)
        Lower left block matrix.
    b : 1-D array, shape(n,)
        Vector of constant terms of the system of linear equations.
    k : int
        B-spline degree.
        
    Returns
    -------
    c : 1-D array, shape(n,)
        Solution of the original system of linear equations.
        
    Notes
    -----
    This algorithm works only for systems with banded matrix A plus
    a correction term U @ V.T, where the matrix U @ V.T gives upper right
    and lower left block of A
    The system is solved with the following steps:
        1.  New systems of linear equations are constructed:
            A @ z_i = u_i,
            u_i - columnn vector of U,
            i = 1, ..., k - 1
        2.  Matrix Z is formed from vectors z_i:
            Z = [ z_1 | z_2 | ... | z_{k - 1} ]
        3.  Matrix H = (1 + V.T @ Z)^{-1}
        4.  The system A' @ y = b is solved
        5.  x = y - Z @ (H @ V.T @ y)
    Also, ``n`` should be greater than ``k``, otherwise corner block
    elements will intersect with diagonals.

    Examples
    --------
    Consider the case of n = 8, k = 5 (size of blocks - 2 x 2).
    The matrix of a system:       U:          V:
      x  x  x  *  *  a  b         a b 0 0     0 0 1 0
      x  x  x  x  *  *  c         0 c 0 0     0 0 0 1
      x  x  x  x  x  *  *         0 0 0 0     0 0 0 0
      *  x  x  x  x  x  *         0 0 0 0     0 0 0 0
      *  *  x  x  x  x  x         0 0 0 0     0 0 0 0
      d  *  *  x  x  x  x         0 0 d 0     1 0 0 0
      e  f  *  *  x  x  x         0 0 e f     0 1 0 0

    References
    ----------
    .. [1] William H. Press, Saul A. Teukolsky, William T. Vetterling
           and Brian P. Flannery, Numerical Recipes, 2007, Section 2.7.3

    r5   r	   N)intr>   r   rg   Zaranger   r   identity)Aurllro   r+   Zk_modbsrF   UZVTZHr(   r;   r   r   r   _woodbury_algorithmF  s   =r   c                 C   s   t | }t|}|d dkr$t |}|dd  |dd d 8  < t |}t |d|  }|||| < td|D ]/}|||  |||d   d   ||| d < || | d  |||d    || | < q>|S )z+
    returns vector of nodes on circle
    r5   r   r	   rO   N)r   r   r1   rB   rg   r*   )r    r+   ZxcrF   Zdxr'   r$   r   r   r   r     s   

 
..r   c                 C   sZ  t tj| ||f\} }}| j}t|| d || d f}t|d D ]A}tj||| d ||d d}||d|d f  |7  < tj||| d || d |d ddd }||| df  |8  < q$t|D ]/}| | }||| kry|}	nt||d }	t||||	}|||| d |	| |	d f< qjtj	dg|d  |f }
t
||
}|S )a/  
    Returns a solution of a system for B-spline interpolation with periodic
    boundary conditions. First ``k - 1`` rows of matrix are condtions of
    periodicity (continuity of ``k - 1`` derivatives at the boundary points).
    Last ``n`` rows are interpolation conditions.
    RHS is ``k - 1`` zeros and ``n`` ordinates in this case.

    Parameters
    ----------
    x : 1-D array, shape (n,)
        Values of x - coordinate of a given set of points.
    y : 1-D array, shape (n,)
        Values of y - coordinate of a given set of points.
    t : 1-D array, shape(n+2*k,)
        Vector of knots.
    k : int
        The maximum degree of spline

    Returns
    -------
    c : 1-D array, shape (n+k-1,)
        B-spline coefficients

    Notes
    -----
    ``t`` is supposed to be taken on circle.

    r	   r   )r_   NrO   )mapr   r:   rZ   rg   r*   r
   Zevaluate_all_bsplZsearchsortedrP   r   )r    r(   r'   r+   rF   Zmatrr$   ZbbZxvalrw   ro   r;   r   r   r   _make_interp_per_full_matr  s$   ,"
r   c              
   C   sh  |j d }t|j dd }|||}t|| d |f}||kr[t|D ]}	t| |dd|	f |||dd|	f< q't||| d f|j dd  }tj	|||d|dS t
|| d }
t|d }tjd| d |
ftjdd	}t||f}t|}tj| ||||d
 || |d d  dddf }t|D ]1}	|tj||	 d |	|f |	d7 }|tj||	| |d  |d d|  |	 f |	 d7 }q|dd|| | f }t|D ]/}	t||||dd|	f dd |}t|| d ||d||d   f|dd|	f< qt||| d f|j dd  }tj	|||d|dS )a  
    Compute the (coefficients of) interpolating B-spline with periodic
    boundary conditions.

    Parameters
    ----------
    x : array_like, shape (n,)
        Abscissas.
    y : array_like, shape (n,)
        Ordinates.
    k : int
        B-spline degree.
    t : array_like, shape (n + 2 * k,).
        Knots taken on a circle, ``k`` on the left and ``k`` on the right
        of the vector ``x``.

    Returns
    -------
    b : a BSpline object of the degree ``k`` and with knots ``t``.

    Notes
    -----
    The original system is formed by ``n + k - 1`` equations where the first
    ``k - 1`` of them stand for the ``k - 1`` derivatives continuity on the
    edges while the other equations correspond to an interpolating case
    (matching all the input points). Due to a special form of knot vector, it
    can be proved that in the original system the first and last ``k``
    coefficients of a spline function are the same, respectively. It follows
    from the fact that all ``k - 1`` derivatives are equal term by term at ends
    and that the matrix of the original system of linear equations is
    non-degenerate. So, we can reduce the number of equations to ``n - 1``
    (first ``k - 1`` equations could be reduced). Another trick of this
    implementation is cyclic shift of values of B-splines due to equality of
    ``k`` unknown coefficients. With this we can receive matrix of the system
    with upper right and lower left blocks, and ``k`` diagonals.  It allows
    to use Woodbury formula to optimize the computations.

    r   r	   Nr4   rf   r5      Fr   Zorderoffset)r+   rO   )r>   r   r^   r   rg   r*   r   r   r   rL   r1   r   r   rQ   r
   _collocZdiagr   Zconcatenate)r    r(   r'   r+   r@   rF   extradimZy_newr;   r$   rV   Zkulabr   r   r   Zccr   r   r   _make_periodic_spline  s2   
'((
"$<$8(r   r   Tc              
   C   s  |du s|dks|dkrd\}}n%t |tr||}}nz|\}}W n ty5 }	 ztd| |	d}	~	ww t|}t||j}t| |} t||}t	||d}|dkrftj
|d |d dd	sftd
|dkrtdd |||fD rztdtj| | d f }t|}
tj|
t|
jd}
tj||
||dS |dkr|du r|du r|du stdtj| d | | d f }t|}
tj|
t|
jd}
tj||
||dS t|}|dkr|durtd|du r6|du r1|du r1|dkrt| |}n9|dkr+| dd | dd  d }tj| d f|d  |dd | d f|d  f }nt| |}nt| |}t||}| jdksQt| dd | dd k rUtdt| dd | dd kritd|dk rrtd|jdkst|dd |dd k rtd| j|jd krtd| j|j|j| j| d k rtd|j| j| d f | d || k s| d ||  krtd|  |dkrt| ||||S t||jdd }t|\}}|jd }t||jdd }t|\}}|jd }| j}|j| d }|| || kr&td|| ||f | }}tjd| | d |ftjdd}t j!| ||||d |dkrWt "||| d |||| |dkrmt j"||| d |||||| d t#|jdd }tj$||f|jd}|dkr|%d||d|< |%d||||| < |dkr|%d|||| d< |rt&tj'||f\}}t(d ||f\}|||||d!d!d"\}}}
}|dkrt)d#|dk rtd$|  t|
%|f|jdd  }
tj||
||dS )%a	  Compute the (coefficients of) interpolating B-spline.

    Parameters
    ----------
    x : array_like, shape (n,)
        Abscissas.
    y : array_like, shape (n, ...)
        Ordinates.
    k : int, optional
        B-spline degree. Default is cubic, k=3.
    t : array_like, shape (nt + k + 1,), optional.
        Knots.
        The number of knots needs to agree with the number of datapoints and
        the number of derivatives at the edges. Specifically, ``nt - n`` must
        equal ``len(deriv_l) + len(deriv_r)``.
    bc_type : 2-tuple or None
        Boundary conditions.
        Default is None, which means choosing the boundary conditions
        automatically. Otherwise, it must be a length-two tuple where the first
        element sets the boundary conditions at ``x[0]`` and the second
        element sets the boundary conditions at ``x[-1]``. Each of these must
        be an iterable of pairs ``(order, value)`` which gives the values of
        derivatives of specified orders at the given edge of the interpolation
        interval.
        Alternatively, the following string aliases are recognized:

        * ``"clamped"``: The first derivatives at the ends are zero. This is
           equivalent to ``bc_type=([(1, 0.0)], [(1, 0.0)])``.
        * ``"natural"``: The second derivatives at ends are zero. This is
          equivalent to ``bc_type=([(2, 0.0)], [(2, 0.0)])``.
        * ``"not-a-knot"`` (default): The first and second segments are the
          same polynomial. This is equivalent to having ``bc_type=None``.
        * ``"periodic"``: The values and the first ``k-1`` derivatives at the
          ends are equivalent.

    axis : int, optional
        Interpolation axis. Default is 0.
    check_finite : bool, optional
        Whether to check that the input arrays contain only finite numbers.
        Disabling may give a performance gain, but may result in problems
        (crashes, non-termination) if the inputs do contain infinities or NaNs.
        Default is True.

    Returns
    -------
    b : a BSpline object of the degree ``k`` and with knots ``t``.

    Examples
    --------

    Use cubic interpolation on Chebyshev nodes:

    >>> def cheb_nodes(N):
    ...     jj = 2.*np.arange(N) + 1
    ...     x = np.cos(np.pi * jj / 2 / N)[::-1]
    ...     return x

    >>> x = cheb_nodes(20)
    >>> y = np.sqrt(1 - x**2)

    >>> from scipy.interpolate import BSpline, make_interp_spline
    >>> b = make_interp_spline(x, y)
    >>> np.allclose(b(x), y)
    True

    Note that the default is a cubic spline with a not-a-knot boundary condition

    >>> b.k
    3

    Here we use a 'natural' spline, with zero 2nd derivatives at edges:

    >>> l, r = [(2, 0.0)], [(2, 0.0)]
    >>> b_n = make_interp_spline(x, y, bc_type=(l, r))  # or, bc_type="natural"
    >>> np.allclose(b_n(x), y)
    True
    >>> x0, x1 = x[0], x[-1]
    >>> np.allclose([b_n(x0, 2), b_n(x1, 2)], [0, 0])
    True

    Interpolation of parametric curves is also supported. As an example, we
    compute a discretization of a snail curve in polar coordinates

    >>> phi = np.linspace(0, 2.*np.pi, 40)
    >>> r = 0.3 + np.cos(phi)
    >>> x, y = r*np.cos(phi), r*np.sin(phi)  # convert to Cartesian coordinates

    Build an interpolating curve, parameterizing it by the angle

    >>> from scipy.interpolate import make_interp_spline
    >>> spl = make_interp_spline(phi, np.c_[x, y])

    Evaluate the interpolant on a finer grid (note that we transpose the result
    to unpack it into a pair of x- and y-arrays)

    >>> phi_new = np.linspace(0, 2.*np.pi, 100)
    >>> x_new, y_new = spl(phi_new).T

    Plot the result

    >>> import matplotlib.pyplot as plt
    >>> plt.plot(x, y, 'o')
    >>> plt.plot(x_new, y_new, '-')
    >>> plt.show()

    Build a B-spline curve with 2 dimensional y
    
    >>> x = np.linspace(0, 2*np.pi, 10)
    >>> y = np.array([np.sin(x), np.cos(x)])

    Periodic condition is satisfied because y coordinates of points on the ends
    are equivalent

    >>> ax = plt.axes(projection='3d')
    >>> xx = np.linspace(0, 2*np.pi, 100)
    >>> bspl = make_interp_spline(x, y, k=5, bc_type='periodic', axis=1)
    >>> ax.plot3D(xx, *bspl(xx))
    >>> ax.scatter3D(x, *y, color='red')
    >>> plt.show()

    See Also
    --------
    BSpline : base class representing the B-spline objects
    CubicSpline : a cubic spline in the polynomial basis
    make_lsq_spline : a similar factory function for spline fitting
    UnivariateSpline : a wrapper over FITPACK spline fitting routines
    splrep : a wrapper over FITPACK spline fitting routines

    Nry   r4   )NNr}   r   rO   gV瞯<)ZatolzAFirst and last points does not match while periodic case expectedc                 s   s    | ]}|d uV  qd S r   r   )r#   _r   r   r   	<genexpr>  s    z%make_interp_spline.<locals>.<genexpr>z6Too much info for k=0: t and bc_type can only be None.r   r@   r	   z0Too much info for k=1: bc_type can only be None.zOFor periodic case t is constructed automatically and can not be passed manuallyr5   g       @'Expect x to be a 1-D sorted array_like.zExpect x to not have duplicatesExpect non-negative k.'Expect t to be a 1-D sorted array_like.(Shapes of x {} and y {} are incompatiblezGot %d knots, need at least %d.Out of bounds w/ x = %s.zNThe number of derivatives at boundaries does not match: expected %s, got %s+%sr   r   r   )gbsvT)overwrite_aboverwrite_bzCollocation matix is singular.z0illegal value in %d-th argument of internal gbsv)*r~   r   r   r   r   r:   r   r?   r"   rA   ZallcloserC   rP   r   r   r   r   rL   r8   r9   r   r   r   r   rZ   r>   formatr   r   r   rg   r   r
   r   Z_handle_lhs_derivativesr   r[   r^   r   Zasarray_chkfiniter   r   )r    r(   r+   r'   r   r@   r!   Zderiv_lZderiv_rr   r;   Zderiv_l_ordsZderiv_l_valsZnleftZderiv_r_ordsZderiv_r_valsZnrightrF   rV   klZkur   r   rhsr   ZluZpivinfor   r   r   r   E  s    




 







, 
,&


"






 r   c              	   C   s6  t | |} t ||}t ||}|durt ||}nt| }t|}t||j}t||d}| jdksFt| dd | dd  dkrJt	d| j
d |d k rWt	d|dk r_t	d|jdksut|dd |dd  dk ryt	d| j|j
d krt	d	| j
|j
|dkrt| || k | ||  kB rt	d
|  | j|jkrt	d| j
|j
|j| d }d}t|j
dd }	tj|d |ftjdd}
tj||	f|jdd}t| |||d|	||
| ||f|j
dd  }t|
d||d}t||f|d|d}t|}tj||||dS )a+  Compute the (coefficients of) an LSQ B-spline.

    The result is a linear combination

    .. math::

            S(x) = \sum_j c_j B_j(x; t)

    of the B-spline basis elements, :math:`B_j(x; t)`, which minimizes

    .. math::

        \sum_{j} \left( w_j \times (S(x_j) - y_j) \right)^2

    Parameters
    ----------
    x : array_like, shape (m,)
        Abscissas.
    y : array_like, shape (m, ...)
        Ordinates.
    t : array_like, shape (n + k + 1,).
        Knots.
        Knots and data points must satisfy Schoenberg-Whitney conditions.
    k : int, optional
        B-spline degree. Default is cubic, k=3.
    w : array_like, shape (n,), optional
        Weights for spline fitting. Must be positive. If ``None``,
        then weights are all equal.
        Default is ``None``.
    axis : int, optional
        Interpolation axis. Default is zero.
    check_finite : bool, optional
        Whether to check that the input arrays contain only finite numbers.
        Disabling may give a performance gain, but may result in problems
        (crashes, non-termination) if the inputs do contain infinities or NaNs.
        Default is True.

    Returns
    -------
    b : a BSpline object of the degree `k` with knots `t`.

    Notes
    -----

    The number of data points must be larger than the spline degree `k`.

    Knots `t` must satisfy the Schoenberg-Whitney conditions,
    i.e., there must be a subset of data points ``x[j]`` such that
    ``t[j] < x[j] < t[j+k+1]``, for ``j=0, 1,...,n-k-2``.

    Examples
    --------
    Generate some noisy data:

    >>> rng = np.random.default_rng()
    >>> x = np.linspace(-3, 3, 50)
    >>> y = np.exp(-x**2) + 0.1 * rng.standard_normal(50)

    Now fit a smoothing cubic spline with a pre-defined internal knots.
    Here we make the knot vector (k+1)-regular by adding boundary knots:

    >>> from scipy.interpolate import make_lsq_spline, BSpline
    >>> t = [-1, 0, 1]
    >>> k = 3
    >>> t = np.r_[(x[0],)*(k+1),
    ...           t,
    ...           (x[-1],)*(k+1)]
    >>> spl = make_lsq_spline(x, y, t, k)

    For comparison, we also construct an interpolating spline for the same
    set of data:

    >>> from scipy.interpolate import make_interp_spline
    >>> spl_i = make_interp_spline(x, y)

    Plot both:

    >>> import matplotlib.pyplot as plt
    >>> xs = np.linspace(-3, 3, 100)
    >>> plt.plot(x, y, 'ro', ms=5)
    >>> plt.plot(xs, spl(xs), 'g-', lw=3, label='LSQ spline')
    >>> plt.plot(xs, spl_i(xs), 'b-', lw=3, alpha=0.7, label='interp spline')
    >>> plt.legend(loc='best')
    >>> plt.show()

    **NaN handling**: If the input arrays contain ``nan`` values, the result is
    not useful since the underlying spline fitting routines cannot deal with
    ``nan``. A workaround is to use zero weights for not-a-number data points:

    >>> y[8] = np.nan
    >>> w = np.isnan(y)
    >>> y[w] = 0.
    >>> tck = make_lsq_spline(x, y, t, w=~w)

    Notice the need to replace a ``nan`` by a numerical value (precise value
    does not matter as long as the corresponding weight is zero.)

    See Also
    --------
    BSpline : base class representing the B-spline objects
    make_interp_spline : a similar factory function for interpolating splines
    LSQUnivariateSpline : a FITPACK-based spline fitting routine
    splrep : a FITPACK-based fitting routine

    Nr   r	   rO   r   zNeed more x points.r   r   r   r   z(Shapes of x {} and w {} are incompatibleTr   r   )r   lowerr!   )r   r!   r   )r"   r   Z	ones_liker8   r9   r   r?   rA   rC   r   r>   rZ   r   r   rg   r   r   r
   Z_norm_eq_lsqr^   r   r   r   r   rL   )r    r(   r'   r+   wr@   r!   rF   r   r   r   r   Z
cho_decompr;   r   r   r   r   T  s\   
j



,,(


r   )F)r   NNr   T)r   Nr   T))r8   Znumpyr   Znumpy.core.multiarrayr   Zscipy.linalgr   r   r   r   r   r    r
   r   r   rl   Zscipy._lib._utilr   Zscipy.specialr   Zscipy.sparser   	itertoolsr   __all__r   r"   r,   r3   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   <module>   sH     
     _U>X
  