o
    h                     @   s   d dl Zd dlmZ d dlmZmZmZ dgZG dd deZ	G dd deZ
G dd	 d	eZG d
d deZG dd dZdS )    N)LinearOperator)kroneye	dia_arrayLaplacianNdc                       s   e Zd ZdZdejd fdd
Zdd Zdd	d
Zdd Z	dd Z
dddZdd Zdd Zdd Zdd Zdd Zdd Z  ZS )r   ai"  
    The grid Laplacian in ``N`` dimensions and its eigenvalues/eigenvectors.

    Construct Laplacian on a uniform rectangular grid in `N` dimensions
    and output its eigenvalues and eigenvectors.
    The Laplacian ``L`` is square, negative definite, real symmetric array
    with signed integer entries and zeros otherwise.

    Parameters
    ----------
    grid_shape : tuple
        A tuple of integers of length ``N`` (corresponding to the dimension of
        the Lapacian), where each entry gives the size of that dimension. The
        Laplacian matrix is square of the size ``np.prod(grid_shape)``.
    boundary_conditions : {'neumann', 'dirichlet', 'periodic'}, optional
        The type of the boundary conditions on the boundaries of the grid.
        Valid values are ``'dirichlet'`` or ``'neumann'``(default) or
        ``'periodic'``.
    dtype : dtype
        Numerical type of the array. Default is ``np.int8``.

    Methods
    -------
    toarray()
        Construct a dense array from Laplacian data
    tosparse()
        Construct a sparse array from Laplacian data
    eigenvalues(m=None)
        Construct a 1D array of `m` largest (smallest in absolute value)
        eigenvalues of the Laplacian matrix in ascending order.
    eigenvectors(m=None):
        Construct the array with columns made of `m` eigenvectors (``float``)
        of the ``Nd`` Laplacian corresponding to the `m` ordered eigenvalues.

    .. versionadded:: 1.12.0

    Notes
    -----
    Compared to the MATLAB/Octave implementation [1] of 1-, 2-, and 3-D
    Laplacian, this code allows the arbitrary N-D case and the matrix-free
    callable option, but is currently limited to pure Dirichlet, Neumann or
    Periodic boundary conditions only.

    The Laplacian matrix of a graph (`scipy.sparse.csgraph.laplacian`) of a
    rectangular grid corresponds to the negative Laplacian with the Neumann
    conditions, i.e., ``boundary_conditions = 'neumann'``.

    All eigenvalues and eigenvectors of the discrete Laplacian operator for
    an ``N``-dimensional  regular grid of shape `grid_shape` with the grid
    step size ``h=1`` are analytically known [2].

    References
    ----------
    .. [1] https://github.com/lobpcg/blopex/blob/master/blopex_tools/matlab/laplacian/laplacian.m
    .. [2] "Eigenvalues and eigenvectors of the second derivative", Wikipedia
           https://en.wikipedia.org/wiki/Eigenvalues_and_eigenvectors_of_the_second_derivative

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.sparse.linalg import LaplacianNd
    >>> from scipy.sparse import diags, csgraph
    >>> from scipy.linalg import eigvalsh

    The one-dimensional Laplacian demonstrated below for pure Neumann boundary
    conditions on a regular grid with ``n=6`` grid points is exactly the
    negative graph Laplacian for the undirected linear graph with ``n``
    vertices using the sparse adjacency matrix ``G`` represented by the
    famous tri-diagonal matrix:

    >>> n = 6
    >>> G = diags(np.ones(n - 1), 1, format='csr')
    >>> Lf = csgraph.laplacian(G, symmetrized=True, form='function')
    >>> grid_shape = (n, )
    >>> lap = LaplacianNd(grid_shape, boundary_conditions='neumann')
    >>> np.array_equal(lap.matmat(np.eye(n)), -Lf(np.eye(n)))
    True

    Since all matrix entries of the Laplacian are integers, ``'int8'`` is
    the default dtype for storing matrix representations.

    >>> lap.tosparse()
    <DIAgonal sparse array of dtype 'int8'
        with 16 stored elements (3 diagonals) and shape (6, 6)>
    >>> lap.toarray()
    array([[-1,  1,  0,  0,  0,  0],
           [ 1, -2,  1,  0,  0,  0],
           [ 0,  1, -2,  1,  0,  0],
           [ 0,  0,  1, -2,  1,  0],
           [ 0,  0,  0,  1, -2,  1],
           [ 0,  0,  0,  0,  1, -1]], dtype=int8)
    >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray())
    True
    >>> np.array_equal(lap.tosparse().toarray(), lap.toarray())
    True

    Any number of extreme eigenvalues and/or eigenvectors can be computed.
    
    >>> lap = LaplacianNd(grid_shape, boundary_conditions='periodic')
    >>> lap.eigenvalues()
    array([-4., -3., -3., -1., -1.,  0.])
    >>> lap.eigenvalues()[-2:]
    array([-1.,  0.])
    >>> lap.eigenvalues(2)
    array([-1.,  0.])
    >>> lap.eigenvectors(1)
    array([[0.40824829],
           [0.40824829],
           [0.40824829],
           [0.40824829],
           [0.40824829],
           [0.40824829]])
    >>> lap.eigenvectors(2)
    array([[ 0.5       ,  0.40824829],
           [ 0.        ,  0.40824829],
           [-0.5       ,  0.40824829],
           [-0.5       ,  0.40824829],
           [ 0.        ,  0.40824829],
           [ 0.5       ,  0.40824829]])
    >>> lap.eigenvectors()
    array([[ 0.40824829,  0.28867513,  0.28867513,  0.5       ,  0.5       ,
             0.40824829],
           [-0.40824829, -0.57735027, -0.57735027,  0.        ,  0.        ,
             0.40824829],
           [ 0.40824829,  0.28867513,  0.28867513, -0.5       , -0.5       ,
             0.40824829],
           [-0.40824829,  0.28867513,  0.28867513, -0.5       , -0.5       ,
             0.40824829],
           [ 0.40824829, -0.57735027, -0.57735027,  0.        ,  0.        ,
             0.40824829],
           [-0.40824829,  0.28867513,  0.28867513,  0.5       ,  0.5       ,
             0.40824829]])

    The two-dimensional Laplacian is illustrated on a regular grid with
    ``grid_shape = (2, 3)`` points in each dimension.

    >>> grid_shape = (2, 3)
    >>> n = np.prod(grid_shape)

    Numeration of grid points is as follows:

    >>> np.arange(n).reshape(grid_shape + (-1,))
    array([[[0],
            [1],
            [2]],
    <BLANKLINE>
           [[3],
            [4],
            [5]]])

    Each of the boundary conditions ``'dirichlet'``, ``'periodic'``, and
    ``'neumann'`` is illustrated separately; with ``'dirichlet'``

    >>> lap = LaplacianNd(grid_shape, boundary_conditions='dirichlet')
    >>> lap.tosparse()
    <Compressed Sparse Row sparse array of dtype 'int8'
        with 20 stored elements and shape (6, 6)>
    >>> lap.toarray()
    array([[-4,  1,  0,  1,  0,  0],
           [ 1, -4,  1,  0,  1,  0],
           [ 0,  1, -4,  0,  0,  1],
           [ 1,  0,  0, -4,  1,  0],
           [ 0,  1,  0,  1, -4,  1],
           [ 0,  0,  1,  0,  1, -4]], dtype=int8)
    >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray())
    True
    >>> np.array_equal(lap.tosparse().toarray(), lap.toarray())
    True
    >>> lap.eigenvalues()
    array([-6.41421356, -5.        , -4.41421356, -3.58578644, -3.        ,
           -1.58578644])
    >>> eigvals = eigvalsh(lap.toarray().astype(np.float64))
    >>> np.allclose(lap.eigenvalues(), eigvals)
    True
    >>> np.allclose(lap.toarray() @ lap.eigenvectors(),
    ...             lap.eigenvectors() @ np.diag(lap.eigenvalues()))
    True

    with ``'periodic'``

    >>> lap = LaplacianNd(grid_shape, boundary_conditions='periodic')
    >>> lap.tosparse()
    <Compressed Sparse Row sparse array of dtype 'int8'
        with 24 stored elements and shape (6, 6)>
    >>> lap.toarray()
        array([[-4,  1,  1,  2,  0,  0],
               [ 1, -4,  1,  0,  2,  0],
               [ 1,  1, -4,  0,  0,  2],
               [ 2,  0,  0, -4,  1,  1],
               [ 0,  2,  0,  1, -4,  1],
               [ 0,  0,  2,  1,  1, -4]], dtype=int8)
    >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray())
    True
    >>> np.array_equal(lap.tosparse().toarray(), lap.toarray())
    True
    >>> lap.eigenvalues()
    array([-7., -7., -4., -3., -3.,  0.])
    >>> eigvals = eigvalsh(lap.toarray().astype(np.float64))
    >>> np.allclose(lap.eigenvalues(), eigvals)
    True
    >>> np.allclose(lap.toarray() @ lap.eigenvectors(),
    ...             lap.eigenvectors() @ np.diag(lap.eigenvalues()))
    True

    and with ``'neumann'``

    >>> lap = LaplacianNd(grid_shape, boundary_conditions='neumann')
    >>> lap.tosparse()
    <Compressed Sparse Row sparse array of dtype 'int8'
        with 20 stored elements and shape (6, 6)>
    >>> lap.toarray()
    array([[-2,  1,  0,  1,  0,  0],
           [ 1, -3,  1,  0,  1,  0],
           [ 0,  1, -2,  0,  0,  1],
           [ 1,  0,  0, -2,  1,  0],
           [ 0,  1,  0,  1, -3,  1],
           [ 0,  0,  1,  0,  1, -2]], dtype=int8)
    >>> np.array_equal(lap.matmat(np.eye(n)), lap.toarray())
    True
    >>> np.array_equal(lap.tosparse().toarray(), lap.toarray())
    True
    >>> lap.eigenvalues()
    array([-5., -3., -3., -2., -1.,  0.])
    >>> eigvals = eigvalsh(lap.toarray().astype(np.float64))
    >>> np.allclose(lap.eigenvalues(), eigvals)
    True
    >>> np.allclose(lap.toarray() @ lap.eigenvectors(),
    ...             lap.eigenvectors() @ np.diag(lap.eigenvalues()))
    True

    neumann)boundary_conditionsdtypec                   sF   |dvrt d|d|| _|| _t|}t j|||fd d S )N)	dirichletr   periodiczUnknown value zv is given for 'boundary_conditions' parameter. The valid options are 'dirichlet', 'periodic', and 'neumann' (default).)r	   shape)
ValueError
grid_shaper   npprodsuper__init__)selfr   r   r	   N	__class__ ^/var/www/vscode/kcb/lib/python3.10/site-packages/scipy/sparse/linalg/_special_sparse_arrays.pyr      s   

zLaplacianNd.__init__c              
   C   s@  | j }|du rt|}t|}nt|tt|| }t|}t|}t||D ]O\}}| jdkrM|dt	tj
|d  d|d   d  7 }q-| jdkre|dt	tj
| d|  d  7 }q-|dt	tj
t|d d  | d  7 }q-| }t|}	||	 }
|dur|
| d }
|	| d }	|
|	fS )zCompute `m` largest eigenvalues in each of the ``N`` directions,
        i.e., up to ``m * N`` total, order them and return `m` largest.
        Nr
         r   )r   r   indiceszerosmintuple	ones_likezipr   sinpifloorravelargsort)r   mr   r   Leiggrid_shape_minjn
Leig_ravelindeigenvaluesr   r   r   _eigenvalue_ordering  s,   



.
&0
z LaplacianNd._eigenvalue_orderingNc                 C   s   |  |\}}|S )a  Return the requested number of eigenvalues.
        
        Parameters
        ----------
        m : int, optional
            The positive number of smallest eigenvalues to return.
            If not provided, then all eigenvalues will be returned.
            
        Returns
        -------
        eigenvalues : float array
            The requested `m` smallest or all eigenvalues, in ascending order.
        )r/   )r   r'   r.   _r   r   r   r.   %  s   zLaplacianNd.eigenvaluesc                 C   s\  | j dkr&tjt|d  |d  }td|d  t||d   }nx| j dkrLtjt|d  | }t|dkr?dnd| t||  }nR|dkr]td| t| }nA|d |kr{|d dkr{td| tdd	g|d  }n#dtj t|d  | }td| t|t	|d d   }d
|t
|ttjjk < |S )zjReturn 1 eigenvector in 1d with index `j`
        and number of grid points `n` where ``j < n``. 
        r
   r   g       @      ?r         ?r   r   g        )r   r   r#   arangesqrtr"   cosonestiler$   absfinfofloat64eps)r   r*   r+   ievr   r   r   _ev1d6  s   
&
*$*zLaplacianNd._ev1dc                    sR    fddt | jD }|d }|dd D ]
}tj||dd}qt| S )z{Return 1 eigenvector in Nd with multi-index `j`
        as a tensor product of the corresponding 1d eigenvectors. 
        c                    s   g | ]
\}}  ||qS r   )r?   ).0r*   r+   r   r   r   
<listcomp>Q  s    z(LaplacianNd._one_eve.<locals>.<listcomp>r   r   N)axes)r!   r   r   	tensordotasarrayr%   )r   kphiresultr   rA   r   _one_eveM  s
   zLaplacianNd._one_evec                    st     |\}}|du r j}nt jtt j| }t||}dd t| D } fdd|D }t|S )a  Return the requested number of eigenvectors for ordered eigenvalues.
        
        Parameters
        ----------
        m : int, optional
            The positive number of eigenvectors to return. If not provided,
            then all eigenvectors will be returned.
            
        Returns
        -------
        eigenvectors : float array
            An array with columns made of the requested `m` or all eigenvectors.
            The columns are ordered according to the `m` ordered eigenvalues. 
        Nc                 S   s   g | ]}t |qS r   )r   r@   xr   r   r   rB   n  s    z,LaplacianNd.eigenvectors.<locals>.<listcomp>c                    s   g | ]}  |qS r   )rI   )r@   rF   rA   r   r   rB   o  s    )	r/   r   r   r   r   r    unravel_indexr!   column_stack)r   r'   r0   r-   r)   	N_indiceseigenvectors_listr   rA   r   eigenvectorsW  s   
zLaplacianNd.eigenvectorsc              
   C   sh  | j }t|}tj||gtjd}t|}t|}t|D ]\}}d|dd< dtd|d|d|f dd< dtd|d|d d|f dd< dtd|d|d|d f dd< | jdkryd|d	< d||d |d f< n*| jd
kr|dkr|d|d f  d7  < ||d df  d7  < n|d	  d7  < |}|dkrt|d| }	t	d|	D ]$}
|d|d|f ||
| |
d | |
| |
d | f< ||7 }q|d|d|f |d|d|f< t
t||d d }	d|d|d|f< dd t	|	D }|d|d|f |||	||	fdd|dd|f< ||7 }q || jS )z
        Converts the Laplacian data to a dense array.

        Returns
        -------
        L : ndarray
            The shape is ``(N, N)`` where ``N = np.prod(grid_shape)``.

        r	   r   Nzii->ir   r   r3   r   r   r   c                 S   s   g | ]}|qS r   r   rJ   r   r   r   rB     s    z'LaplacianNd.toarray.<locals>.<listcomp>)r   r   r   r   int8
empty_like	enumerateeinsumr   rangeintreshapeastyper	   )r   r   r+   LL_iLtempr-   dimnew_dimtilesr*   idxr   r   r   toarrayr  sL   



$((

<
$
zLaplacianNd.toarrayc           
      C   sZ  t | j}t| j}t||ftjd}t|D ]}| j| }tjd|gtjd}|dddf  d9  < | jdkrBd|d< d|d	< t|g d
f||ftjd}| jdkrwt||ftjd}|j	dg| d d |j	dg|d d ||7 }t|D ]}	t
t| j|	 tjd|}q{t|d |D ]}	t
|t| j|	 tjd}q||7 }q|| jS )a)  
        Constructs a sparse array from the Laplacian data. The returned sparse
        array format is dependent on the selected boundary conditions.

        Returns
        -------
        L : scipy.sparse.sparray
            The shape is ``(N, N)`` where ``N = np.prod(grid_shape)``.

        rQ      r   NrR   r   r3   r   r   )r   r3   r3   r   r   r   r	   r   )rF   )lenr   r   r   r   rT   rX   r7   r   setdiagr   r   r[   r	   )
r   r   pr\   r=   r_   datar]   tr*   r   r   r   tosparse  s0   




zLaplacianNd.tosparsec              	   C   s(  | j }t|}||d }d| | }t|D ]}|tj|d|d7 }|tj|d|d7 }| jdv r
|td f| d td f|| d     tj|d|dtd f| d td f|| d    8  < |td f| d td f|| d     tj|d|dtd f| d td f|| d    8  < | jdkr
|td f| d td f|| d     tj|d	|dtd f| d td f|| d    7  < |td f| d td f|| d     tj|d	|dtd f| d td f|| d    7  < q|d|jd S )
N)r3   rR   r   )axisr3   )r   r
   )r   r   r   )	r   rh   rZ   rX   r   rollr   slicer   )r   rK   r   r   XYr=   r   r   r   _matvec  sJ   ,&&&&&&&zLaplacianNd._matvecc                 C   
   |  |S Nrs   r   rK   r   r   r   _matmat  s   
zLaplacianNd._matmatc                 C      | S ru   r   rA   r   r   r   _adjoint     zLaplacianNd._adjointc                 C   ry   ru   r   rA   r   r   r   
_transpose  r{   zLaplacianNd._transposeru   )__name__
__module____qualname____doc__r   rT   r   r/   r.   r?   rI   rP   rc   rm   rs   rx   rz   r|   __classcell__r   r   r   r   r   
   s"     l


@)!c                       sh   e Zd ZdZejf fdd	Z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  ZS )Sakuraia  
    Construct a Sakurai matrix in various formats and its eigenvalues.

    Constructs the "Sakurai" matrix motivated by reference [1]_:
    square real symmetric positive definite and 5-diagonal
    with the main diagonal ``[5, 6, 6, ..., 6, 6, 5], the ``+1`` and ``-1``
    diagonals filled with ``-4``, and the ``+2`` and ``-2`` diagonals
    made of ``1``. Its eigenvalues are analytically known to be
    ``16. * np.power(np.cos(0.5 * k * np.pi / (n + 1)), 4)``.
    The matrix gets ill-conditioned with its size growing.
    It is useful for testing and benchmarking sparse eigenvalue solvers
    especially those taking advantage of its banded 5-diagonal structure.
    See the notes below for details.

    Parameters
    ----------
    n : int
        The size of the matrix.
    dtype : dtype
        Numerical type of the array. Default is ``np.int8``.

    Methods
    -------
    toarray()
        Construct a dense array from Laplacian data
    tosparse()
        Construct a sparse array from Laplacian data
    tobanded()
        The Sakurai matrix in the format for banded symmetric matrices,
        i.e., (3, n) ndarray with 3 upper diagonals
        placing the main diagonal at the bottom.
    eigenvalues
        All eigenvalues of the Sakurai matrix ordered ascending.

    Notes
    -----
    Reference [1]_ introduces a generalized eigenproblem for the matrix pair
    `A` and `B` where `A` is the identity so we turn it into an eigenproblem
    just for the matrix `B` that this function outputs in various formats
    together with its eigenvalues.
    
    .. versionadded:: 1.12.0

    References
    ----------
    .. [1] T. Sakurai, H. Tadano, Y. Inadomi, and U. Nagashima,
       "A moment-based method for large-scale generalized
       eigenvalue problems",
       Appl. Num. Anal. Comp. Math. Vol. 1 No. 2 (2004).

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.sparse.linalg._special_sparse_arrays import Sakurai
    >>> from scipy.linalg import eig_banded
    >>> n = 6
    >>> sak = Sakurai(n)

    Since all matrix entries are small integers, ``'int8'`` is
    the default dtype for storing matrix representations.

    >>> sak.toarray()
    array([[ 5, -4,  1,  0,  0,  0],
           [-4,  6, -4,  1,  0,  0],
           [ 1, -4,  6, -4,  1,  0],
           [ 0,  1, -4,  6, -4,  1],
           [ 0,  0,  1, -4,  6, -4],
           [ 0,  0,  0,  1, -4,  5]], dtype=int8)
    >>> sak.tobanded()
    array([[ 1,  1,  1,  1,  1,  1],
           [-4, -4, -4, -4, -4, -4],
           [ 5,  6,  6,  6,  6,  5]], dtype=int8)
    >>> sak.tosparse()
    <DIAgonal sparse array of dtype 'int8'
        with 24 stored elements (5 diagonals) and shape (6, 6)>
    >>> np.array_equal(sak.dot(np.eye(n)), sak.tosparse().toarray())
    True
    >>> sak.eigenvalues()
    array([0.03922866, 0.56703972, 2.41789479, 5.97822974,
           10.54287655, 14.45473055])
    >>> sak.eigenvalues(2)
    array([0.03922866, 0.56703972])

    The banded form can be used in scipy functions for banded matrices, e.g.,

    >>> e = eig_banded(sak.tobanded(), eigvals_only=True)
    >>> np.allclose(sak.eigenvalues, e, atol= n * n * n * np.finfo(float).eps)
    True

    c                    s&   || _ || _||f}t || d S ru   )r+   r	   r   r   )r   r+   r	   r   r   r   r   r   a  s   zSakurai.__init__Nc              
   C   sZ   |du r| j }t| j d | | j d }tdttd| tj | j d  d S )a  Return the requested number of eigenvalues.
        
        Parameters
        ----------
        m : int, optional
            The positive number of smallest eigenvalues to return.
            If not provided, then all eigenvalues will be returned.
            
        Returns
        -------
        eigenvalues : `np.float64` array
            The requested `m` smallest or all eigenvalues, in ascending order.
        Nr   g      0@r2      )r+   r   r4   flippowerr6   r#   )r   r'   rF   r   r   r   r.   g  s   0zSakurai.eigenvaluesc                 C   sf   t jddt j| jd | jd df }dt j| j| jd }t j| j| jd}t |||g| jS )zA
        Construct the Sakurai matrix as a banded array.
              r   rQ   r   )r   r_r7   r+   r	   arrayr[   )r   d0d1d2r   r   r   tobandedz  s   &zSakurai.tobandedc                 C   sH   ddl m} |  }||d |d |d |d |d gg d| j| jS )zB
        Construct the Sakurai matrix is a sparse format.
        r   )spdiagsr   r   )rR   r3   r   r   r   )scipy.sparser   r   r+   )r   r   dr   r   r   rm     s
   (zSakurai.tosparsec                 C      |    S ru   rm   rc   rA   r   r   r   rc        zSakurai.toarrayc                 C   sD  | | jd}t|j| j}tj||d}d|dddf  d|dddf   |dddf  |dddf< d|dddf  d|d	ddf   |d
ddf  |dddf< d|ddddf  d|dd	ddf |ddddf    t|dd
ddf d t|ddddf d |ddddf< |S )z
        Construct matrix-free callable banded-matrix-vector multiplication by
        the Sakurai matrix without constructing or storing the matrix itself
        using the knowledge of its entries and the 5-diagonal format.
        r3   rQ   r   r   Nr   r   r   rR   r   )re   rS   rd   ))r   r   rS   )rZ   r+   r   promote_typesr	   
zeros_likepad)r   rK   result_dtypesxr   r   r   rs     s   DDBzSakurai._matvecc                 C   rt   )z
        Construct matrix-free callable matrix-matrix multiplication by
        the Sakurai matrix without constructing or storing the matrix itself
        by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``.
        rv   rw   r   r   r   rx        
zSakurai._matmatc                 C   ry   ru   r   rA   r   r   r   rz     r{   zSakurai._adjointc                 C   ry   ru   r   rA   r   r   r   r|     r{   zSakurai._transposeru   )r}   r~   r   r   r   rT   r   r.   r   rm   rc   rs   rx   rz   r|   r   r   r   r   r   r     s    Z
	r   c                       sf   e Zd ZdZejf f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  ZS )MikotaMas  
    Construct a mass matrix in various formats of Mikota pair.

    The mass matrix `M` is square real diagonal
    positive definite with entries that are reciprocal to integers.

    Parameters
    ----------
    shape : tuple of int
        The shape of the matrix.
    dtype : dtype
        Numerical type of the array. Default is ``np.float64``.

    Methods
    -------
    toarray()
        Construct a dense array from Mikota data
    tosparse()
        Construct a sparse array from Mikota data
    tobanded()
        The format for banded symmetric matrices,
        i.e., (1, n) ndarray with the main diagonal.
    c                    s   || _ || _t || d S ru   )r   r	   r   r   )r   r   r	   r   r   r   r     s   zMikotaM.__init__c                 C   s"   dt d| jd d  | jS )Nr1   r   r   )r   r4   r   r[   r	   rA   r   r   r   _diag  s   "zMikotaM._diagc                 C   s   |   S ru   )r   rA   r   r   r   r     s   zMikotaM.tobandedc                 C   s(   ddl m} ||  gdg| j| jdS )Nr   diagsrg   )r   r   r   r   r	   r   r   r   r   r   rm     s   zMikotaM.tosparsec                 C   s   t |  | jS ru   )r   diagr   r[   r	   rA   r   r   r   rc     s   zMikotaM.toarrayc                 C   s,   | | jd d}|  ddtjf | S )z
        Construct matrix-free callable banded-matrix-vector multiplication by
        the Mikota mass matrix without constructing or storing the matrix itself
        using the knowledge of its entries and the diagonal format.
        r   r3   N)rZ   r   r   r   newaxisrw   r   r   r   rs     s   zMikotaM._matvecc                 C   rt   )z
        Construct matrix-free callable matrix-matrix multiplication by
        the Mikota mass matrix without constructing or storing the matrix itself
        by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``.
        rv   rw   r   r   r   rx     r   zMikotaM._matmatc                 C   ry   ru   r   rA   r   r   r   rz     r{   zMikotaM._adjointc                 C   ry   ru   r   rA   r   r   r   r|     r{   zMikotaM._transpose)r}   r~   r   r   r   r;   r   r   r   rm   rc   rs   rx   rz   r|   r   r   r   r   r   r     s    	r   c                       s^   e Zd ZdZejf fdd	Zdd Zdd Zdd	 Z	d
d Z
dd Zdd Zdd Z  ZS )MikotaKa  
    Construct a stiffness matrix in various formats of Mikota pair.

    The stiffness matrix `K` is square real tri-diagonal symmetric
    positive definite with integer entries. 

    Parameters
    ----------
    shape : tuple of int
        The shape of the matrix.
    dtype : dtype
        Numerical type of the array. Default is ``np.int32``.

    Methods
    -------
    toarray()
        Construct a dense array from Mikota data
    tosparse()
        Construct a sparse array from Mikota data
    tobanded()
        The format for banded symmetric matrices,
        i.e., (2, n) ndarray with 2 upper diagonals
        placing the main diagonal at the bottom.
    c                    s`   || _ || _t || |d }tjd| d dd| jd| _tj|d dd| jd | _d S )Nr   r   r   rR   rQ   r3   )r   r	   r   r   r   r4   _diag0_diag1)r   r   r	   r+   r   r   r   r     s    zMikotaK.__init__c                 C   s   t t | jdd| jgS )Nre   constant)r   r   r   r   r   rA   r   r   r   r     s   zMikotaK.tobandedc                 C   s0   ddl m} || j| j| jgg d| j| jdS )Nr   r   rf   rg   )r   r   r   r   r   r	   r   r   r   r   rm     s   zMikotaK.tosparsec                 C   r   ru   r   rA   r   r   r   rc     r   zMikotaK.toarrayc                 C   s4  | | jd d}t|j| j}tj||d}| j}| j}|d |dddf  |d |dddf   |dddf< |d |dddf  |d |dddf   |dddf< |dddf |ddddf  |dddf |ddddf   |dddf |ddddf   |ddddf< |S )z
        Construct matrix-free callable banded-matrix-vector multiplication by
        the Mikota stiffness matrix without constructing or storing the matrix
        itself using the knowledge of its entries and the 3-diagonal format.
        r   r3   rQ   Nr   rR   r   )rZ   r   r   r   r	   r   r   r   )r   rK   r   kxr   r   r   r   r   rs   "  s   <<"""zMikotaK._matvecc                 C   rt   )z
        Construct matrix-free callable matrix-matrix multiplication by
        the Stiffness mass matrix without constructing or storing the matrix itself
        by reusing the ``_matvec(x)`` that supports both 1D and 2D arrays ``x``.
        rv   rw   r   r   r   rx   4  r   zMikotaK._matmatc                 C   ry   ru   r   rA   r   r   r   rz   <  r{   zMikotaK._adjointc                 C   ry   ru   r   rA   r   r   r   r|   ?  r{   zMikotaK._transpose)r}   r~   r   r   r   int32r   r   rm   rc   rs   rx   rz   r|   r   r   r   r   r   r     s    
r   c                   @   s(   e Zd ZdZejfddZdddZdS )
MikotaPaira  
    Construct the Mikota pair of matrices in various formats and
    eigenvalues of the generalized eigenproblem with them.

    The Mikota pair of matrices [1, 2]_ models a vibration problem
    of a linear mass-spring system with the ends attached where
    the stiffness of the springs and the masses increase along
    the system length such that vibration frequencies are subsequent
    integers 1, 2, ..., `n` where `n` is the number of the masses. Thus,
    eigenvalues of the generalized eigenvalue problem for
    the matrix pair `K` and `M` where `K` is the system stiffness matrix
    and `M` is the system mass matrix are the squares of the integers,
    i.e., 1, 4, 9, ..., ``n * n``.

    The stiffness matrix `K` is square real tri-diagonal symmetric
    positive definite. The mass matrix `M` is diagonal with diagonal
    entries 1, 1/2, 1/3, ...., ``1/n``. Both matrices get
    ill-conditioned with `n` growing.

    Parameters
    ----------
    n : int
        The size of the matrices of the Mikota pair.
    dtype : dtype
        Numerical type of the array. Default is ``np.float64``.

    Attributes
    ----------
    eigenvalues : 1D ndarray, ``np.uint64``
        All eigenvalues of the Mikota pair ordered ascending.

    Methods
    -------
    MikotaK()
        A `LinearOperator` custom object for the stiffness matrix.
    MikotaM()
        A `LinearOperator` custom object for the mass matrix.
    
    .. versionadded:: 1.12.0

    References
    ----------
    .. [1] J. Mikota, "Frequency tuning of chain structure multibody oscillators
       to place the natural frequencies at omega1 and N-1 integer multiples
       omega2,..., omegaN", Z. Angew. Math. Mech. 81 (2001), S2, S201-S202.
       Appl. Num. Anal. Comp. Math. Vol. 1 No. 2 (2004).
    .. [2] Peter C. Muller and Metin Gurgoze,
       "Natural frequencies of a multi-degree-of-freedom vibration system",
       Proc. Appl. Math. Mech. 6, 319-320 (2006).
       http://dx.doi.org/10.1002/pamm.200610141.

    Examples
    --------
    >>> import numpy as np
    >>> from scipy.sparse.linalg._special_sparse_arrays import MikotaPair
    >>> n = 6
    >>> mik = MikotaPair(n)
    >>> mik_k = mik.k
    >>> mik_m = mik.m
    >>> mik_k.toarray()
    array([[11., -5.,  0.,  0.,  0.,  0.],
           [-5.,  9., -4.,  0.,  0.,  0.],
           [ 0., -4.,  7., -3.,  0.,  0.],
           [ 0.,  0., -3.,  5., -2.,  0.],
           [ 0.,  0.,  0., -2.,  3., -1.],
           [ 0.,  0.,  0.,  0., -1.,  1.]])
    >>> mik_k.tobanded()
    array([[ 0., -5., -4., -3., -2., -1.],
           [11.,  9.,  7.,  5.,  3.,  1.]])
    >>> mik_m.tobanded()
    array([1.        , 0.5       , 0.33333333, 0.25      , 0.2       ,
        0.16666667])
    >>> mik_k.tosparse()
    <DIAgonal sparse array of dtype 'float64'
        with 20 stored elements (3 diagonals) and shape (6, 6)>
    >>> mik_m.tosparse()
    <DIAgonal sparse array of dtype 'float64'
        with 6 stored elements (1 diagonals) and shape (6, 6)>
    >>> np.array_equal(mik_k(np.eye(n)), mik_k.toarray())
    True
    >>> np.array_equal(mik_m(np.eye(n)), mik_m.toarray())
    True
    >>> mik.eigenvalues()
    array([ 1,  4,  9, 16, 25, 36])  
    >>> mik.eigenvalues(2)
    array([ 1,  4])

    c                 C   s:   || _ || _||f| _t| j| j| _t| j| j| _d S ru   )r+   r	   r   r   r'   r   rF   )r   r+   r	   r   r   r   r     s
   
zMikotaPair.__init__Nc                 C   s,   |du r| j }tjd|d tjd}|| S )a  Return the requested number of eigenvalues.
        
        Parameters
        ----------
        m : int, optional
            The positive number of smallest eigenvalues to return.
            If not provided, then all eigenvalues will be returned.
            
        Returns
        -------
        eigenvalues : `np.uint64` array
            The requested `m` smallest or all eigenvalues, in ascending order.
        Nr   rQ   )r+   r   r4   uint64)r   r'   arange_plus1r   r   r   r.     s   zMikotaPair.eigenvaluesru   )r}   r~   r   r   r   r;   r   r.   r   r   r   r   r   C  s    Xr   )numpyr   scipy.sparse.linalgr   r   r   r   r   __all__r   r   r   r   r   r   r   r   r   <module>   s        +DO