o
    ohB                     @   s   d Z ddl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mZmZ dd	lmZ dd
lmZ e
dkrFddgZG dd de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! Z"d"d# Z#d$d% Z$d&d' Z%d(S ))z

Module for the SDM class.

    )addnegpossubmul)defaultdict)GROUND_TYPES)doctest_depends_on)_strongly_connected_components   )DMBadInputErrorDMDomainErrorDMShapeError)QQ)DDMflint
SDM.to_dfmSDM.to_dfm_or_ddmc                       s  e Zd ZdZdZdZdZ fddZdd Zdd	 Z	d
d Z
dd Zdd Zdd Zedd Zdd Zedd Zedd Zdd Zdd Zedd Zd d! Zed"d# Zd$d% Zed&d' Zd(d) Zed*d+ Zd,d- Zd.d/ Zd0d1 Zd2d3 Ze d4gd5d6d7 Z!e d4gd5d8d9 Z"ed:d; Z#ed<d= Z$ed>d? Z%eddAdBZ&dCdD Z'dEdF Z(dGdH Z)dIdJ Z*dKdL Z+dMdN Z,dOdP Z-dQdR Z.dSdT Z/dUdV Z0dWdX Z1dYdZ Z2d[d\ Z3d]d^ Z4d_d` Z5dadb Z6dcdd Z7dedf Z8dgdh Z9didj Z:dkdl Z;dmdn Z<dodp Z=ddqdrZ>dsdt Z?dudv Z@dwdx ZAdydz ZBd{d| ZCd}d~ ZDdd ZEdd ZFdd ZGdd ZHeIddfddZJeIddfddZK  ZLS )SDMa  Sparse matrix based on polys domain elements

    This is a dict subclass and is a wrapper for a dict of dicts that supports
    basic matrix arithmetic +, -, *, **.


    In order to create a new :py:class:`~.SDM`, a dict
    of dicts mapping non-zero elements to their
    corresponding row and column in the matrix is needed.

    We also need to specify the shape and :py:class:`~.Domain`
    of our :py:class:`~.SDM` object.

    We declare a 2x2 :py:class:`~.SDM` matrix belonging
    to QQ domain as shown below.
    The 2x2 Matrix in the example is

    .. math::
           A = \left[\begin{array}{ccc}
                0 & \frac{1}{2} \\
                0 & 0 \end{array} \right]


    >>> from sympy.polys.matrices.sdm import SDM
    >>> from sympy import QQ
    >>> elemsdict = {0:{1:QQ(1, 2)}}
    >>> A = SDM(elemsdict, (2, 2), QQ)
    >>> A
    {0: {1: 1/2}}

    We can manipulate :py:class:`~.SDM` the same way
    as a Matrix class

    >>> from sympy import ZZ
    >>> A = SDM({0:{1: ZZ(2)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
    >>> B  = SDM({0:{0: ZZ(3)}, 1:{1:ZZ(4)}}, (2, 2), ZZ)
    >>> A + B
    {0: {0: 3, 1: 2}, 1: {0: 1, 1: 4}}

    Multiplication

    >>> A*B
    {0: {1: 8}, 1: {0: 3}}
    >>> A*ZZ(2)
    {0: {1: 4}, 1: {0: 2}}

    sparseFc                    sp   t  | | | _ \| _| _\ || _t fdd| D s%tdtfdd|  D s6tdd S )Nc                 3   s(    | ]}d |  ko k n  V  qdS r   N ).0r)mr   l/var/www/html/construction_image-detection-poc/venv/lib/python3.10/site-packages/sympy/polys/matrices/sdm.py	<genexpr>S   s   & zSDM.__init__.<locals>.<genexpr>zRow out of rangec                 3   s2    | ]}|D ]}d |  ko k n  V  qqdS r   r   )r   rowcnr   r   r   U   s   0 zColumn out of range)	super__init__shaperowscolsdomainallr   values)self	elemsdictr#   r&   	__class__)r   r    r   r"   N   s   zSDM.__init__c                 C   s   z| | | W S  t yT   | j\}}| |  kr|k rPn td| |  kr.|k rPn tdz| ||  ||  W  Y S  t yO   | jj Y  Y S w tdw Nzindex out of range)KeyErrorr#   r&   zero
IndexError)r)   ijr   r    r   r   r   getitemX   s   
zSDM.getitemc                 C   s   | j \}}| |  kr|k r$n td| |  kr#|k s(td td|| || }}|rMz	|| | |< W d S  tyL   ||i| |< Y d S w | |d }|d urnz||= W n
 tyf   Y d S w |sp| |= d S d S d S r-   )r#   r0   r.   get)r)   r1   r2   valuer   r    rowir   r   r   setiteme   s2   


zSDM.setitemc           	         s   | j \}}t|| }t||  i }|  D ]\}}||v r3 fdd| D }|r3||||< q| |t|t f| jS )Nc                    s$   i | ]\}}| v r  ||qS r   )indexr   r2   ecir   r   
<dictcomp>      $ z%SDM.extract_slice.<locals>.<dictcomp>)r#   rangeitemsr8   newlenr&   )	r)   slice1slice2r   r    risdmr1   r   r   r;   r   extract_slicez   s   
zSDM.extract_slicec                 C   s  | r|r|s|  t|t|f| jS | j\}}| t|  kr/t|  kr/|k s4td td| t|  krKt|  krK|k sPtd tdtt}tt}t	|D ]\}}|||  
| q\t	|D ]\}	}
||
|  
|	 qnt|}t|}| }i }|| @ D ]/}|| }i }|| @ D ]}
||
 }||
 D ]}	|||	< qq|r|| D ]}| ||< qq| |t|t|f| jS )NzRow index out of rangezColumn index out of range)zerosrB   r&   r#   minmaxr0   r   list	enumerateappendsetkeyscopyrA   )r)   r$   r%   r   r    rowmapcolmapi2i1j2j1rowsetcolsetsdm1sdm2row1row2row1_j1r   r   r   extract   sD   
&&
zSDM.extractc                 C   sN   g }|   D ]\}}ddd |  D }|d||f  qdd| S )Nz, c                 s   s     | ]\}}d ||f V  qdS )z%s: %sNr   )r   r2   elemr   r   r   r          zSDM.__str__.<locals>.<genexpr>z%s: {%s}z{%s})r@   joinrM   )r)   rowsstrr1   r   elemsstrr   r   r   __str__   s
   zSDM.__str__c                 C   s(   t | j}t| }d||| j| jf S )Nz%s(%s, %s, %s))type__name__dict__repr__r#   r&   )r)   clsr$   r   r   r   rh      s   

zSDM.__repr__c                 C   s   | |||S )a  

        Parameters
        ==========

        sdm: A dict of dicts for non-zero elements in SDM
        shape: tuple representing dimension of SDM
        domain: Represents :py:class:`~.Domain` of SDM

        Returns
        =======

        An :py:class:`~.SDM` object

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> elemsdict = {0:{1: QQ(2)}}
        >>> A = SDM.new(elemsdict, (2, 2), QQ)
        >>> A
        {0: {1: 2}}

        r   )ri   rF   r#   r&   r   r   r   rA      s   zSDM.newc                 C   s$   dd |   D }| || j| jS )aT  
        Returns the copy of a :py:class:`~.SDM` object

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> elemsdict = {0:{1:QQ(2)}, 1:{}}
        >>> A = SDM(elemsdict, (2, 2), QQ)
        >>> B = A.copy()
        >>> B
        {0: {1: 2}, 1: {}}

        c                 S      i | ]	\}}||  qS r   rP   r   r1   Air   r   r   r=          zSDM.copy.<locals>.<dictcomp>)r@   rA   r#   r&   )AAcr   r   r   rP      s   zSDM.copyc                    sp   |\}t  |krtfdd D std fddfddt|D }dd |D }| |||S )	a"  
        Create :py:class:`~.SDM` object from a list of lists.

        Parameters
        ==========

        ddm:
            list of lists containing domain elements
        shape:
            Dimensions of :py:class:`~.SDM` matrix
        domain:
            Represents :py:class:`~.Domain` of :py:class:`~.SDM` object

        Returns
        =======

        :py:class:`~.SDM` containing elements of ddm

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> ddm = [[QQ(1, 2), QQ(0)], [QQ(0), QQ(3, 4)]]
        >>> A = SDM.from_list(ddm, (2, 2), QQ)
        >>> A
        {0: {0: 1/2}, 1: {1: 3/4}}

        See Also
        ========

        to_list
        from_list_flat
        from_dok
        from_ddm
        c                 3   s    | ]	}t | kV  qd S N)rB   )r   r   r   r   r   r         z SDM.from_list.<locals>.<genexpr>zInconsistent row-list/shapec                    s    fddt D S )Nc                    s&   i | ]}  | r|  | qS r   r   r   r2   )ddmr1   r   r   r=     s   & z3SDM.from_list.<locals>.<lambda>.<locals>.<dictcomp>)r?   r1   )rt   r    ru   r   <lambda>  s    zSDM.from_list.<locals>.<lambda>c                 3   s    | ]	}| |fV  qd S rq   r   r   r1   )getrowr   r   r     rr   c                 S   s   i | ]	\}}|r||qS r   r   r   r1   r   r   r   r   r=     rn   z!SDM.from_list.<locals>.<dictcomp>)rB   r'   r   r?   )ri   rt   r#   r&   r   irowsrF   r   )rt   rx   r    r   	from_list   s   '"zSDM.from_listc                 C   s   |  ||j|jS )a1  
        Create :py:class:`~.SDM` from a :py:class:`~.DDM`.

        Examples
        ========

        >>> from sympy.polys.matrices.ddm import DDM
        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> ddm = DDM( [[QQ(1, 2), 0], [0, QQ(3, 4)]], (2, 2), QQ)
        >>> A = SDM.from_ddm(ddm)
        >>> A
        {0: {0: 1/2}, 1: {1: 3/4}}
        >>> SDM.from_ddm(ddm).to_ddm() == ddm
        True

        See Also
        ========

        to_ddm
        from_list
        from_list_flat
        from_dok
        )r{   r#   r&   )ri   rt   r   r   r   from_ddm  s   zSDM.from_ddmc                    s^   | j \} | jj fddt|D }|  D ]\}}| D ]
\}}||| |< q!q|S )aL  
        Convert a :py:class:`~.SDM` object to a list of lists.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> elemsdict = {0:{1:QQ(2)}, 1:{}}
        >>> A = SDM(elemsdict, (2, 2), QQ)
        >>> A.to_list()
        [[0, 2], [0, 0]]


        c                    s   g | ]}g  qS r   r   )r   _r    r/   r   r   
<listcomp>G      zSDM.to_list.<locals>.<listcomp>)r#   r&   r/   r?   r@   )Mr   rt   r1   r   r2   r:   r   r~   r   to_list5  s   
zSDM.to_listc           	      C   sX   | j \}}| jj}|g||  }|  D ]\}}| D ]\}}|||| | < qq|S )a  
        Convert :py:class:`~.SDM` to a flat list.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0:{1:QQ(2)}, 1:{0: QQ(3)}}, (2, 2), QQ)
        >>> A.to_list_flat()
        [0, 2, 3, 0]
        >>> A == A.from_list_flat(A.to_list_flat(), A.shape, A.domain)
        True

        See Also
        ========

        from_list_flat
        to_list
        to_dok
        to_ddm
        r#   r&   r/   r@   )	r   r   r    r/   flatr1   r   r2   r:   r   r   r   to_list_flatM  s   
zSDM.to_list_flatc                 C   sd   |\}}t ||| krtdtt}t|D ]\}}|r+t||\}	}
|||	 |
< q| |||S )a  
        Create :py:class:`~.SDM` from a flat list of elements.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM.from_list_flat([QQ(0), QQ(2), QQ(0), QQ(0)], (2, 2), QQ)
        >>> A
        {0: {1: 2}}
        >>> A == A.from_list_flat(A.to_list_flat(), A.shape, A.domain)
        True

        See Also
        ========

        to_list_flat
        from_list
        from_dok
        from_ddm
        zInconsistent flat-list shape)rB   r   r   rg   rL   divmod)ri   elementsr#   r&   r   r    rF   injelementr1   r2   r   r   r   from_list_flatl  s   zSDM.from_list_flatc                 C   s.   |   }t|}t| }|| jf}||fS )aq  
        Convert :class:`SDM` to a flat list of nonzero elements and data.

        Explanation
        ===========

        This is used to operate on a list of the elements of a matrix and then
        reconstruct a modified matrix with elements in the same positions using
        :meth:`from_flat_nz`. Zero elements are omitted from the list.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0:{1:QQ(2)}, 1:{0: QQ(3)}}, (2, 2), QQ)
        >>> elements, data = A.to_flat_nz()
        >>> elements
        [2, 3]
        >>> A == A.from_flat_nz(elements, data, A.domain)
        True

        See Also
        ========

        from_flat_nz
        to_list_flat
        sympy.polys.matrices.ddm.DDM.to_flat_nz
        sympy.polys.matrices.domainmatrix.DomainMatrix.to_flat_nz
        )to_doktuplerK   r(   r#   )r   dokindicesr   datar   r   r   
to_flat_nz  s
   
zSDM.to_flat_nzc                 C   s$   |\}}t t||}| |||S )aE  
        Reconstruct a :class:`~.SDM` after calling :meth:`to_flat_nz`.

        See :meth:`to_flat_nz` for explanation.

        See Also
        ========

        to_flat_nz
        from_list_flat
        sympy.polys.matrices.ddm.DDM.from_flat_nz
        sympy.polys.matrices.domainmatrix.DomainMatrix.from_flat_nz
        )rg   zipfrom_dok)ri   r   r   r&   r   r#   r   r   r   r   from_flat_nz  s   zSDM.from_flat_nzc                 C      dd |   D S )a  
        Convert to dictionary of dictionaries (dod) format.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0: {1: QQ(2)}, 1: {0: QQ(3)}}, (2, 2), QQ)
        >>> A.to_dod()
        {0: {1: 2}, 1: {0: 3}}

        See Also
        ========

        from_dod
        sympy.polys.matrices.domainmatrix.DomainMatrix.to_dod
        c                 S   rj   r   rk   ry   r   r   r   r=     rn   zSDM.to_dod.<locals>.<dictcomp>r@   r   r   r   r   to_dod  s   z
SDM.to_dodc           	      C   sH   t t}| D ]\}}| D ]\}}|r||| |< qq| |||S )a  
        Create :py:class:`~.SDM` from dictionary of dictionaries (dod) format.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> dod = {0: {1: QQ(2)}, 1: {0: QQ(3)}}
        >>> A = SDM.from_dod(dod, (2, 2), QQ)
        >>> A
        {0: {1: 2}, 1: {0: 3}}
        >>> A == SDM.from_dod(A.to_dod(), A.shape, A.domain)
        True

        See Also
        ========

        to_dod
        sympy.polys.matrices.domainmatrix.DomainMatrix.to_dod
        r   rg   r@   )	ri   dodr#   r&   rF   r1   r   r2   r:   r   r   r   from_dod  s   zSDM.from_dodc                 C   r   )a  
        Convert to dictionary of keys (dok) format.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0: {1: QQ(2)}, 1: {0: QQ(3)}}, (2, 2), QQ)
        >>> A.to_dok()
        {(0, 1): 2, (1, 0): 3}

        See Also
        ========

        from_dok
        to_list
        to_list_flat
        to_ddm
        c                 S   s,   i | ]\}}|  D ]	\}}||f|q
qS r   r   )r   r1   r   r2   r:   r   r   r   r=     s   , zSDM.to_dok.<locals>.<dictcomp>r   r   r   r   r   r     s   z
SDM.to_dokc                 C   s:   t t}| D ]\\}}}|r||| |< q| |||S )a  
        Create :py:class:`~.SDM` from dictionary of keys (dok) format.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> dok = {(0, 1): QQ(2), (1, 0): QQ(3)}
        >>> A = SDM.from_dok(dok, (2, 2), QQ)
        >>> A
        {0: {1: 2}, 1: {0: 3}}
        >>> A == SDM.from_dok(A.to_dok(), A.shape, A.domain)
        True

        See Also
        ========

        to_dok
        from_list
        from_list_flat
        from_ddm
        r   )ri   r   r#   r&   rF   r1   r2   r:   r   r   r   r     s   zSDM.from_dokc                 c   s"    |   D ]	}|  E dH  qdS )a<  
        Iterate over the nonzero values of a :py:class:`~.SDM` matrix.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0: {1: QQ(2)}, 1: {0: QQ(3)}}, (2, 2), QQ)
        >>> list(A.iter_values())
        [2, 3]

        N)r(   )r   r   r   r   r   iter_values/  s   zSDM.iter_valuesc                 c   s8    |   D ]\}}|  D ]\}}||f|fV  qqdS )a  
        Iterate over indices and values of the nonzero elements.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0: {1: QQ(2)}, 1: {0: QQ(3)}}, (2, 2), QQ)
        >>> list(A.iter_items())
        [((0, 1), 2), ((1, 0), 3)]

        See Also
        ========

        sympy.polys.matrices.domainmatrix.DomainMatrix.iter_items
        Nr   )r   r1   r   r2   r:   r   r   r   
iter_items@  s   zSDM.iter_itemsc                 C   s   t |  | j| jS )a2  
        Convert a :py:class:`~.SDM` object to a :py:class:`~.DDM` object

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0:{1:QQ(2)}, 1:{}}, (2, 2), QQ)
        >>> A.to_ddm()
        [[0, 2], [0, 0]]

        )r   r   r#   r&   r   r   r   r   to_ddmV  s   z
SDM.to_ddmc                 C   s   | S )zE
        Convert to :py:class:`~.SDM` format (returns self).
        r   r   r   r   r   to_sdmf  s   z
SDM.to_sdmr   )ground_typesc                 C      |    S )a  
        Convert a :py:class:`~.SDM` object to a :py:class:`~.DFM` object

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0:{1:QQ(2)}, 1:{}}, (2, 2), QQ)
        >>> A.to_dfm()
        [[0, 2], [0, 0]]

        See Also
        ========

        to_ddm
        to_dfm_or_ddm
        sympy.polys.matrices.domainmatrix.DomainMatrix.to_dfm
        )r   to_dfmr   r   r   r   r   l     r   c                 C   r   )a3  
        Convert to :py:class:`~.DFM` if possible, else :py:class:`~.DDM`.

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0:{1:QQ(2)}, 1:{}}, (2, 2), QQ)
        >>> A.to_dfm_or_ddm()
        [[0, 2], [0, 0]]
        >>> type(A.to_dfm_or_ddm())  # depends on the ground types
        <class 'sympy.polys.matrices._dfm.DFM'>

        See Also
        ========

        to_ddm
        to_dfm
        sympy.polys.matrices.domainmatrix.DomainMatrix.to_dfm_or_ddm
        )r   to_dfm_or_ddmr   r   r   r   r     s   r   c                 C   s   | i ||S )a  

        Returns a :py:class:`~.SDM` of size shape,
        belonging to the specified domain

        In the example below we declare a matrix A where,

        .. math::
            A := \left[\begin{array}{ccc}
            0 & 0 & 0 \\
            0 & 0 & 0 \end{array} \right]

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM.zeros((2, 3), QQ)
        >>> A
        {}

        r   )ri   r#   r&   r   r   r   rH     r   z	SDM.zerosc                    sH   |j }|\}}ttt||g|   fddt|D }| |||S )Nc                    s   i | ]}|   qS r   rk   rw   r   r   r   r=     r   zSDM.ones.<locals>.<dictcomp>)onerg   r   r?   )ri   r#   r&   r   r   r    rF   r   r   r   ones  s
   zSDM.onesc                    sP   t |tr||}}n|\}}|j  fddtt||D }| |||f|S )aO  

        Returns a identity :py:class:`~.SDM` matrix of dimensions
        size x size, belonging to the specified domain

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> I = SDM.eye((2, 2), QQ)
        >>> I
        {0: {0: 1}, 1: {1: 1}}

        c                    s   i | ]}|| iqS r   r   rw   r   r   r   r=     r   zSDM.eye.<locals>.<dictcomp>)
isinstanceintr   r?   rI   )ri   r#   r&   r$   r%   rF   r   r   r   eye  s   
zSDM.eyeNc                 C   s6   |d u rt |t |f}dd t|D }| |||S )Nc                 S   s   i | ]\}}|r|||iqS r   r   )r   r1   vr   r   r   r=         zSDM.diag.<locals>.<dictcomp>)rB   rL   )ri   diagonalr&   r#   rF   r   r   r   diag  s   zSDM.diagc                 C   s$   t | }| || jddd | jS )a$  

        Returns the transpose of a :py:class:`~.SDM` matrix

        Examples
        ========

        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy import QQ
        >>> A = SDM({0:{1:QQ(2)}, 1:{}}, (2, 2), QQ)
        >>> A.transpose()
        {1: {0: 2}}

        N)sdm_transposerA   r#   r&   )r   MTr   r   r   	transpose  s   zSDM.transposec                 C   8   t |tstS | j|jkrtd| j|jf | |S )NzMatrix size mismatch: %s + %s)r   r   NotImplementedr#   r   r   ro   Br   r   r   __add__  
   

zSDM.__add__c                 C   r   )NzMatrix size mismatch: %s - %s)r   r   r   r#   r   r   r   r   r   r   __sub__  r   zSDM.__sub__c                 C   s   |   S rq   )r   ro   r   r   r   __neg__  s   zSDM.__neg__c                 C   s,   t |tr
| |S || jv r| |S tS )zA * B)r   r   matmulr&   r   r   r   r   r   r   __mul__  s
   



zSDM.__mul__c                 C   s   || j v r
| |S tS rq   )r&   rmulr   )abr   r   r   __rmul__  s   

zSDM.__rmul__c                 C   sV   | j |j krt| j\}}|j\}}||krtt| || j ||}| |||f| j S )a  
        Performs matrix multiplication of two SDM matrices

        Parameters
        ==========

        A, B: SDM to multiply

        Returns
        =======

        SDM
            SDM after multiplication

        Raises
        ======

        DomainError
            If domain of A does not match
            with that of B

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{1: ZZ(2)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
        >>> B = SDM({0:{0:ZZ(2), 1:ZZ(3)}, 1:{0:ZZ(4)}}, (2, 2), ZZ)
        >>> A.matmul(B)
        {0: {0: 8}, 1: {0: 2, 1: 3}}

        )r&   r   r#   r   
sdm_matmulrA   )ro   r   r   r    n2oCr   r   r   r     s   !

z
SDM.matmulc                    $   t |  fdd}| || j| jS )a.  
        Multiplies each element of A with a scalar b

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{1: ZZ(2)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
        >>> A.mul(ZZ(3))
        {0: {1: 6}, 1: {0: 3}}

        c                    s   |   S rq   r   aijr   r   r   rv   E      zSDM.mul.<locals>.<lambda>	unop_dictrA   r#   r&   ro   r   Csdmr   r   r   r   7  s   zSDM.mulc                    r   )Nc                    s    |  S rq   r   r   r   r   r   rv   I  r   zSDM.rmul.<locals>.<lambda>r   r   r   r   r   r   H  s   zSDM.rmulc                    sV   | j |j krt| j|jkrt| j j  fdd}t| |t||}| || j| j S )Nc                    s    S rq   r   r:   r/   r   r   rv   R  s    z%SDM.mul_elementwise.<locals>.<lambda>)r&   r   r#   r   r/   
binop_dictr   rA   )ro   r   fzeror   r   r   r   mul_elementwiseL  s   zSDM.mul_elementwisec                 C   s"   t | |ttt}| || j| jS )ak  

        Adds two :py:class:`~.SDM` matrices

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{1: ZZ(2)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
        >>> B = SDM({0:{0: ZZ(3)}, 1:{1:ZZ(4)}}, (2, 2), ZZ)
        >>> A.add(B)
        {0: {0: 3, 1: 2}, 1: {0: 1, 1: 4}}

        )r   r   r   rA   r#   r&   ro   r   r   r   r   r   r   V     zSDM.addc                 C   s"   t | |ttt}| || j| jS )as  

        Subtracts two :py:class:`~.SDM` matrices

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{1: ZZ(2)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
        >>> B  = SDM({0:{0: ZZ(3)}, 1:{1:ZZ(4)}}, (2, 2), ZZ)
        >>> A.sub(B)
        {0: {0: -3, 1: 2}, 1: {0: 1, 1: -4}}

        )r   r   r   r   rA   r#   r&   r   r   r   r   r   i  r   zSDM.subc                 C   s   t | t}| || j| jS )a2  

        Returns the negative of a :py:class:`~.SDM` matrix

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{1: ZZ(2)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
        >>> A.neg()
        {0: {1: -2}, 1: {0: -1}}

        )r   r   rA   r#   r&   )ro   r   r   r   r   r   |  s   
zSDM.negc                    s:   | j  kr|  S t|  fdd}| || j S )aN  
        Converts the :py:class:`~.Domain` of a :py:class:`~.SDM` matrix to K

        Examples
        ========

        >>> from sympy import ZZ, QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{1: ZZ(2)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
        >>> A.convert_to(QQ)
        {0: {1: 2}, 1: {0: 1}}

        c                    s     | S rq   )convert_fromr   KKoldr   r   rv     s    z SDM.convert_to.<locals>.<lambda>)r&   rP   r   rA   r#   )ro   r   Akr   r   r   
convert_to  s
   zSDM.convert_toc                 C   s   t tt|  S )ay  Number of non-zero elements in the :py:class:`~.SDM` matrix.

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{1: ZZ(2)}, 1:{0:ZZ(1)}}, (2, 2), ZZ)
        >>> A.nnz()
        2

        See Also
        ========

        sympy.polys.matrices.domainmatrix.DomainMatrix.nnz
        )summaprB   r(   r   r   r   r   nnz  s   zSDM.nnzc                    s:    j \}}||ksJ t|} fdd|D }t||S )a{  Strongly connected components of a square matrix *A*.

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0: ZZ(2)}, 1:{1:ZZ(1)}}, (2, 2), ZZ)
        >>> A.scc()
        [[0], [1]]

        See also
        ========

        sympy.polys.matrices.domainmatrix.DomainMatrix.scc
        c                    s   i | ]}|t  |g qS r   )rK   r4   )r   r   r   r   r   r=     r   zSDM.scc.<locals>.<dictcomp>)r#   r?   r
   )ro   r$   r%   VEmapr   r   r   scc  s
   

zSDM.sccc                 C   s$   t | \}}}| || j| j|fS )a_  

        Returns reduced-row echelon form and list of pivots for the :py:class:`~.SDM`

        Examples
        ========

        >>> from sympy import QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(2), 1:QQ(4)}}, (2, 2), QQ)
        >>> A.rref()
        ({0: {0: 1, 1: 2}}, [0])

        )	sdm_irrefrA   r#   r&   )ro   r   pivotsr}   r   r   r   rref  s   zSDM.rrefc                 C   s2   | j }t| |\}}}| || j| j }|||fS )a]  

        Returns reduced-row echelon form (RREF) with denominator and pivots.

        Examples
        ========

        >>> from sympy import QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(2), 1:QQ(4)}}, (2, 2), QQ)
        >>> A.rref_den()
        ({0: {0: 1, 1: 2}}, 1, [0])

        )r&   sdm_rref_denrA   r#   )ro   r   
A_rref_sdmdenomr   A_rrefr   r   r   rref_den  s   
zSDM.rref_denc                 C   s   |     S )a>  

        Returns inverse of a matrix A

        Examples
        ========

        >>> from sympy import QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
        >>> A.inv()
        {0: {0: -2, 1: 1}, 1: {0: 3/2, 1: -1/2}}

        )r   invr   r   r   r   r   r     s   zSDM.invc                 C   r   )a  
        Returns determinant of A

        Examples
        ========

        >>> from sympy import QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
        >>> A.det()
        -2

        )r   detr   r   r   r   r     r   zSDM.detc                 C   s(   |    \}}}| || ||fS )a`  

        Returns LU decomposition for a matrix A

        Examples
        ========

        >>> from sympy import QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
        >>> A.lu()
        ({0: {0: 1}, 1: {0: 3, 1: 1}}, {0: {0: 1, 1: 2}, 1: {1: -2}}, [])

        )r   lur|   )ro   LUswapsr   r   r   r     s   zSDM.luc                 C   s   |  |  | S )an  

        Uses LU decomposition to solve Ax = b,

        Examples
        ========

        >>> from sympy import QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
        >>> b = SDM({0:{0:QQ(1)}, 1:{0:QQ(2)}}, (2, 1), QQ)
        >>> A.lu_solve(b)
        {1: {0: 1/2}}

        )r|   r   lu_solve)ro   r   r   r   r   r   ,  s   zSDM.lu_solvec           	      C   s`   | j d }| jj}t| \}}}t|||||\}}tt|}t||f}| ||| j|fS )a  
        Nullspace of a :py:class:`~.SDM` matrix A.

        The domain of the matrix must be a field.

        It is better to use the :meth:`~.DomainMatrix.nullspace` method rather
        than this method which is otherwise no longer used.

        Examples
        ========

        >>> from sympy import QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0: QQ(2), 1: QQ(4)}}, (2, 2), QQ)
        >>> A.nullspace()
        ({0: {0: -2, 1: 1}}, [1])


        See Also
        ========

        sympy.polys.matrices.domainmatrix.DomainMatrix.nullspace
            The preferred way to get the nullspace of a matrix.

        r   )	r#   r&   r   r   sdm_nullspace_from_rrefrg   rL   rB   rA   )	ro   ncolsr   r   r   nzcolsr   	nonpivotsr#   r   r   r   	nullspace>  s   
zSDM.nullspacec                 C   sT  | j \}}| j}|du rttt|  }|s%| ||f|tt|fS t	||kr5| 
d|f|g fS | d |d  }||rDJ t|}tt}|  D ]\}}	|	 D ]\}
}||
 ||f qXqPg }g }t|D ]%}
|
|v rvqo||
 |
|i}||
 D ]\}}| ||| < q|| qott|}| |t	||f|}||fS )a\  
        Returns nullspace for a :py:class:`~.SDM` matrix ``A`` in RREF.

        The domain of the matrix can be any domain.

        The matrix must already be in reduced row echelon form (RREF).

        Examples
        ========

        >>> from sympy import QQ
        >>> from sympy.polys.matrices.sdm import SDM
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0: QQ(2), 1: QQ(4)}}, (2, 2), QQ)
        >>> A_rref, pivots = A.rref()
        >>> A_null, nonpivots = A_rref.nullspace_from_rref(pivots)
        >>> A_null
        {0: {0: -2, 1: 1}}
        >>> pivots
        [0]
        >>> nonpivots
        [1]

        See Also
        ========

        sympy.polys.matrices.domainmatrix.DomainMatrix.nullspace
            The higher-level function that would usually be called instead of
            calling this one directly.

        sympy.polys.matrices.domainmatrix.DomainMatrix.nullspace_from_rref
            The higher-level direct equivalent of this function.

        sympy.polys.matrices.ddm.DDM.nullspace_from_rref
            The equivalent function for dense :py:class:`~.DDM` matrices.

        Nr   )r#   r&   sortedr   rI   r(   r   rK   r?   rB   rH   is_zerorN   r   r@   rM   rg   rL   rA   )ro   r   r   r    r   	pivot_val
pivots_setnonzero_colsr1   rm   r2   Aijbasisr   veciprF   A_nullr   r   r   nullspace_from_rref`  s:   
%	
zSDM.nullspace_from_rrefc                 C   sL   | j d }t| \}}}t|||}|rd|ini }| |d|d f| jS )Nr   r   )r#   r   sdm_particular_from_rrefrA   r&   )ro   r   r   r   r   Prepr   r   r   
particular  s
   
zSDM.particularc                 G   s   t |  }| j\}}| j}|D ]@}|j\}}||ksJ |j|ks$J | D ]#\}	}
||	d}|du r<i  ||	< }|
 D ]
\}}|||| < q@q(||7 }q| |||f| jS )a  Horizontally stacks :py:class:`~.SDM` matrices.

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM

        >>> A = SDM({0: {0: ZZ(1), 1: ZZ(2)}, 1: {0: ZZ(3), 1: ZZ(4)}}, (2, 2), ZZ)
        >>> B = SDM({0: {0: ZZ(5), 1: ZZ(6)}, 1: {0: ZZ(7), 1: ZZ(8)}}, (2, 2), ZZ)
        >>> A.hstack(B)
        {0: {0: 1, 1: 2, 2: 5, 3: 6}, 1: {0: 3, 1: 4, 2: 7, 3: 8}}

        >>> C = SDM({0: {0: ZZ(9), 1: ZZ(10)}, 1: {0: ZZ(11), 1: ZZ(12)}}, (2, 2), ZZ)
        >>> A.hstack(B, C)
        {0: {0: 1, 1: 2, 2: 5, 3: 6, 4: 9, 5: 10}, 1: {0: 3, 1: 4, 2: 7, 3: 8, 4: 11, 5: 12}}
        N)rg   rP   r#   r&   r@   r4   rA   )ro   r   Anewr$   r%   r&   BkBkrowsBkcolsr1   Bkirm   r2   Bkijr   r   r   hstack  s    


z
SDM.hstackc                 G   s   t |  }| j\}}| j}|D ]'}|j\}}||ksJ |j|ks$J | D ]
\}	}
|
||	| < q(||7 }q| |||f| jS )a  Vertically stacks :py:class:`~.SDM` matrices.

        Examples
        ========

        >>> from sympy import ZZ
        >>> from sympy.polys.matrices.sdm import SDM

        >>> A = SDM({0: {0: ZZ(1), 1: ZZ(2)}, 1: {0: ZZ(3), 1: ZZ(4)}}, (2, 2), ZZ)
        >>> B = SDM({0: {0: ZZ(5), 1: ZZ(6)}, 1: {0: ZZ(7), 1: ZZ(8)}}, (2, 2), ZZ)
        >>> A.vstack(B)
        {0: {0: 1, 1: 2}, 1: {0: 3, 1: 4}, 2: {0: 5, 1: 6}, 3: {0: 7, 1: 8}}

        >>> C = SDM({0: {0: ZZ(9), 1: ZZ(10)}, 1: {0: ZZ(11), 1: ZZ(12)}}, (2, 2), ZZ)
        >>> A.vstack(B, C)
        {0: {0: 1, 1: 2}, 1: {0: 3, 1: 4}, 2: {0: 5, 1: 6}, 3: {0: 7, 1: 8}, 4: {0: 9, 1: 10}, 5: {0: 11, 1: 12}}
        )rg   rP   r#   r&   r@   rA   )ro   r   r  r$   r%   r&   r  r  r  r1   r	  r   r   r   vstack  s   


z
SDM.vstackc                    s&    fdd|   D }| || j|S )Nc                    s(   i | ]\}}| fd d|  D qS )c                    s   i | ]	\}}| |qS r   r   r9   funcr   r   r=     rn   z,SDM.applyfunc.<locals>.<dictcomp>.<dictcomp>r   ry   r  r   r   r=     s   ( z!SDM.applyfunc.<locals>.<dictcomp>)r@   rA   r#   )r)   r  r&   rF   r   r  r   	applyfunc  s   zSDM.applyfuncc                 C   sJ   | j }| j\}}t| ||}|jg|d  }| D ]\}}|||< q|S )a  
        Returns the coefficients of the characteristic polynomial
        of the :py:class:`~.SDM` matrix. These elements will be domain elements.
        The domain of the elements will be same as domain of the :py:class:`~.SDM`.

        Examples
        ========

        >>> from sympy import QQ, Symbol
        >>> from sympy.polys.matrices.sdm import SDM
        >>> from sympy.polys import Poly
        >>> A = SDM({0:{0:QQ(1), 1:QQ(2)}, 1:{0:QQ(3), 1:QQ(4)}}, (2, 2), QQ)
        >>> A.charpoly()
        [1, -5, -2]

        We can create a polynomial using the
        coefficients using :py:class:`~.Poly`

        >>> x = Symbol('x')
        >>> p = Poly(A.charpoly(), x, domain=A.domain)
        >>> p
        Poly(x**2 - 5*x - 2, x, domain='QQ')

        r   )r&   r#   sdm_berkr/   r@   )ro   r   r    r}   pdictplistr1   pir   r   r   charpoly  s   

zSDM.charpolyc                 C   s   |  S )z@
        Says whether this matrix has all zero entries.
        r   r)   r   r   r   is_zero_matrix)  s   zSDM.is_zero_matrixc                 C      t dd |  D S )z~
        Says whether this matrix is upper-triangular. True can be returned
        even if the matrix is not square.
        c                 s   s&    | ]\}}|D ]}||kV  qqd S rq   r   r   r1   r   r2   r   r   r   r   4     $ zSDM.is_upper.<locals>.<genexpr>r'   r@   r  r   r   r   is_upper/     zSDM.is_upperc                 C   r  )z~
        Says whether this matrix is lower-triangular. True can be returned
        even if the matrix is not square.
        c                 s   s&    | ]\}}|D ]}||kV  qqd S rq   r   r  r   r   r   r   ;  r  zSDM.is_lower.<locals>.<genexpr>r  r  r   r   r   is_lower6  r  zSDM.is_lowerc                 C   r  )zv
        Says whether this matrix is diagonal. True can be returned
        even if the matrix is not square.
        c                 s   s&    | ]\}}|D ]}||kV  qqd S rq   r   r  r   r   r   r   B  r  z"SDM.is_diagonal.<locals>.<genexpr>r  r  r   r   r   is_diagonal=  r  zSDM.is_diagonalc                    s*   | j \} | jj fdd|  D S )z?
        Returns the diagonal of the matrix as a list.
        c                    s$   g | ]\}}| k r| |qS r   )r4   ry   r~   r   r   r   J  r>   z SDM.diagonal.<locals>.<listcomp>r   )r)   r   r   r~   r   r   D  s   
zSDM.diagonal      c                 C   s   |   j|d S )zQ
        Returns the LLL-reduced basis for the :py:class:`~.SDM` matrix.
        delta)r   lllr   )ro   r"  r   r   r   r#  L  s   zSDM.lllc                 C   s$   |   j|d\}}| | fS )zJ
        Returns the LLL-reduced basis and transformation matrix.
        r!  )r   lll_transformr   )ro   r"  reduced	transformr   r   r   r$  R  s   zSDM.lll_transformrq   )Mrf   
__module____qualname____doc__fmtis_DFMis_DDMr"   r3   r7   rG   r^   rd   rh   classmethodrA   rP   r{   r|   r   r   r   r   r   r   r   r   r   r   r   r   r   r	   r   r   rH   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r  r  r  r  r  r  r  r  r  r   r   r#  r$  __classcell__r   r   r+   r   r      s    0
&

.

!%









	*

"W%!!r   c                 C   sl  t | t |}}i }||@ D ]X}| | || }	}
i }t |	t |
}}||@ D ]}||	| |
| }|r:|||< q)|| D ]}||	| }|rM|||< q?|| D ]}||
| }|r`|||< qR|rg|||< q|| D ]!}| | }	i }|	 D ]\}}||}|r|||< qx|r|||< ql|| D ]!}|| }
i }|
 D ]\}}||}|r|||< q|r|||< q|S rq   )rN   r@   )ro   r   fabfafbAnzBnzr   r1   rm   BiCiAnziBnzir2   Cijr   Bijr   r   r   r   Z  s^   r   c           	      C   sP   i }|   D ]\}}i }|  D ]\}}||}|r|||< q|r%|||< q|S rq   r   )	ro   fr   r1   rm   r4  r2   r   r9  r   r   r   r     s   r   c              
   C   sZ   i }|   D ]$\}}|  D ]\}}z||| |< W q ty)   ||i||< Y qw q|S rq   )r@   r.   )r   r   r1   Mir2   Mijr   r   r   r     s   r   c                    s&   |  fdd   @ D S )Nc                 3   s     | ]} | |  V  qd S rq   r   rs   r   r   r   r     r`   zsdm_dotvec.<locals>.<genexpr>)r   rO   )ro   r   r   r   r   r   
sdm_dotvec  s   &r=  c                 C   s2   i }|   D ]\}}t|||}|r|||< q|S rq   )r@   r=  )ro   r   r   r   r1   rm   r5  r   r   r   sdm_matvecmul  s   r>  c                 C   s   |j rt| ||||S i }t|}|  D ]M\}}i }	t|}
|
|@ D ]8}|| }||  D ]+\}}|	|d }|d urP|||  }|rJ||	|< q/|	| q/|| }|rZ||	|< q/q#|	rb|	||< q|S rq   )is_EXRAWsdm_matmul_exrawrN   r@   r4   pop)ro   r   r   r   r   r   B_knzr1   rm   r5  Ai_knzkAikr2   Bkjr8  r   r   r   r     s2   
r   c              
   C   s  |j }i }t|}|  D ]\}}	tt}
t|	}||@ D ]7}|	| }|| |kr>||  D ]\}}|
| ||  q/qt|D ]}|
| ||| ||  qBq|| D ]}||	|  }||krst|D ]	}|
| | qiqYi }|
 D ]\}}||}|r|||< qz|r|||< q| D ]G\}}| D ]>\}}|| |krt|D ]/}| |i ||}||kr||i }|||||  }||kr|||< nt	|||< qqq|S rq   )
r/   rN   r@   r   rK   rM   r?   r4   r   RuntimeError)ro   r   r   r   r   r/   r   rB  r1   rm   Ci_listrC  rD  rE  r2   rF  zAikr5  Cij_listr8  r  r   r   r   r@    sZ    

r@  c                    s  t dd |  D td}i t t }tt}|r4| }fdd| D }|t|@ D ]I}| }|| }t|}t|}	|	| D ]}
| ||
  ||
< qG|| || |	|@ D ]}
||
 |||
   }|rt|||
< qa||
 qaq1|s~qt|}|| }||< t|}|d }|D ]
}||  |9  < q||dD ]h}
|
 }|| }t|}|| D ]}| ||  ||< || |
 q|| || ||@ D ]#}|| |||   }|r|||< q|| ||kr|| |
 qt	|dkr|
 ||
 qt	|dkr| n|| |D ]}||kr0|| | q"|st |B }d	d t
|D   fd
d| D }fdd|D }tt
|}|||fS )a  RREF and pivots of a sparse matrix *A*.

    Compute the reduced row echelon form (RREF) of the matrix *A* and return a
    list of the pivot columns. This routine does not work in place and leaves
    the original matrix *A* unmodified.

    The domain of the matrix must be a field.

    Examples
    ========

    This routine works with a dict of dicts sparse representation of a matrix:

    >>> from sympy import QQ
    >>> from sympy.polys.matrices.sdm import sdm_irref
    >>> A = {0: {0: QQ(1), 1: QQ(2)}, 1: {0: QQ(3), 1: QQ(4)}}
    >>> Arref, pivots, _ = sdm_irref(A)
    >>> Arref
    {0: {0: 1}, 1: {1: 1}}
    >>> pivots
    [0, 1]

    The analogous calculation with :py:class:`~.MutableDenseMatrix` would be

    >>> from sympy import Matrix
    >>> M = Matrix([[1, 2], [3, 4]])
    >>> Mrref, pivots = M.rref()
    >>> Mrref
    Matrix([
    [1, 0],
    [0, 1]])
    >>> pivots
    (0, 1)

    Notes
    =====

    The cost of this algorithm is determined purely by the nonzero elements of
    the matrix. No part of the cost of any step in this algorithm depends on
    the number of rows or columns in the matrix. No step depends even on the
    number of nonzero rows apart from the primary loop over those rows. The
    implementation is much faster than ddm_rref for sparse matrices. In fact
    at the time of writing it is also (slightly) faster than the dense
    implementation even if the input is a fully dense matrix so it seems to be
    faster in all cases.

    The elements of the matrix should support exact division with ``/``. For
    example elements of any domain that is a field (e.g. ``QQ``) should be
    fine. No attempt is made to handle inexact arithmetic.

    See Also
    ========

    sympy.polys.matrices.domainmatrix.DomainMatrix.rref
        The higher-level function that would normally be used to call this
        routine.
    sympy.polys.matrices.dense.ddm_irref
        The dense equivalent of this routine.
    sdm_rref_den
        Fraction-free version of this routine.
    c                 s   s    | ]}|  V  qd S rq   rk   )r   rm   r   r   r   r   r  s    zsdm_irref.<locals>.<genexpr>keyc                       i | ]\}}| vr||qS r   r   r   r2   r   )reduced_pivotsr   r   r=     r   zsdm_irref.<locals>.<dictcomp>r   r   r   c                 S      i | ]\}}||qS r   r   )r   r    pr   r   r   r=     r   c                    s$   i | ]\}}| fd d|D qS )c                    s   h | ]} | qS r   r   )r   rQ  	pivot2rowr   r   	<setcomp>      z'sdm_irref.<locals>.<dictcomp>.<setcomp>r   )r   r   srR  r   r   r=     r>   c                    s   g | ]} | qS r   r   rw   )pivot_row_mapr   r   r     rU  zsdm_irref.<locals>.<listcomp>)r   r(   rI   rN   r   rA  r@   remover   rB   rL   rg   )ro   Arowsnonreduced_pivotsnonzero_columnsrm   r2   Ajr   AinzAjnzrD  rE  Aijinvlr   AkjAknzAklr   r$   r   r   )rS  rW  rO  r   r     s   [


	







K
r   c           $         sJ  | si |j g fS t| dkr%|  \}t|}|| }d| i||gfS |jr,|j}n|j}tt	| 
  \}}i }i }	|  |	 }
g }d}d}t	|td}|D ]} fdd|
 D }i }|
| @ D ]8}||}||	|  }|
 D ]&\}}||}|du r|| ||< qz|||  }|r|||< qz|| qzqit|}t|}|p|j }|| D ]	}||  ||< q|| D ]
}|| | ||< q||@ D ]}|| | ||  }|r|||< q|| q|sqSt|}||}t|	
 D ]\}}|| }||vr%|
 D ]\}}|| }|dur|||}|||< qq||}t|}t|}|| D ]}| ||  ||< |durO||| |||< q6|| D ]}|||  ||< |durm||| |||< qU||@ D ]'}|||  |||   }|r|dur|||}|||< qs|| qs|s|	| |||< qt|}|| |r||	|< n|||< ||s|du r|}n||9 }|dur|||}|}qS|du r|j }i ||	}dd |
 D fdd	t|D } tt	|  \}!}"t|!}!t|"D ]\}}|||!| < qtt|"}#|#||!fS )
a?  
    Return the reduced row echelon form (RREF) of A with denominator.

    The RREF is computed using fraction-free Gauss-Jordan elimination.

    Explanation
    ===========

    The algorithm used is the fraction-free version of Gauss-Jordan elimination
    described as FFGJ in [1]_. Here it is modified to handle zero or missing
    pivots and to avoid redundant arithmetic. This implementation is also
    optimized for sparse matrices.

    The domain $K$ must support exact division (``K.exquo``) but does not need
    to be a field. This method is suitable for most exact rings and fields like
    :ref:`ZZ`, :ref:`QQ` and :ref:`QQ(a)`. In the case of :ref:`QQ` or
    :ref:`K(x)` it might be more efficient to clear denominators and use
    :ref:`ZZ` or :ref:`K[x]` instead.

    For inexact domains like :ref:`RR` and :ref:`CC` use ``ddm_irref`` instead.

    Examples
    ========

    >>> from sympy.polys.matrices.sdm import sdm_rref_den
    >>> from sympy.polys.domains import ZZ
    >>> A = {0: {0: ZZ(1), 1: ZZ(2)}, 1: {0: ZZ(3), 1: ZZ(4)}}
    >>> A_rref, den, pivots = sdm_rref_den(A, ZZ)
    >>> A_rref
    {0: {0: -2}, 1: {1: -2}}
    >>> den
    -2
    >>> pivots
    [0, 1]

    See Also
    ========

    sympy.polys.matrices.domainmatrix.DomainMatrix.rref_den
        Higher-level interface to ``sdm_rref_den`` that would usually be used
        instead of calling this function directly.
    sympy.polys.matrices.sdm.sdm_rref_den
        The ``SDM`` method that uses this function.
    sdm_irref
        Computes RREF using field division.
    ddm_irref_den
        The dense version of this algorithm.

    References
    ==========

    .. [1] Fraction-free algorithms for linear and polynomial equations.
        George C. Nakos , Peter R. Turner , Robert M. Williams.
        https://dl.acm.org/doi/10.1145/271130.271133
    r   r   NrK  c                    rM  r   r   rN  )r%  r   r   r=   Z  r   z sdm_rref_den.<locals>.<dictcomp>c                 S   rP  r   r   )r   r2   r1   r   r   r   r=     r   c                    s   g | ]
\}} | |fqS r   r   rl   )
row_to_colr   r   r     s    z sdm_rref_den.<locals>.<listcomp>)r   rB   r(   rI   rP   is_Exactexquoquor   r   r@   rO   rA  r4   rN   rK   rM   is_onerL   rg   )$ro   r   rm   r2   r   rf  r}   rows_in_ordercol_to_row_reducedcol_to_row_unreduced	unreducedA_rref_rowsr   divisorA_rows	Ai_cancelr\  rD  Ajk
Aik_cancelAi_nzAi_cancel_nzdrE  pkr   r`  rc  ra  Ak_nzr1   
col_to_rowA_rref_rows_colr   r   r   r   )r%  rd  r   r     s   ]
























r   c           
      C   sh   t tt|t| }g }|D ]}||i}||dD ]}	| |	 |  |||	 < q|| q||fS )z%Get nullspace from A which is in RREFr   )r   rN   r?   r4   rM   )
ro   r   r   r   r   r   r   r2   Kjr1   r   r   r   r     s   r   c                 C   sJ   i }t |D ]\}}| | |d d}|dur"|| | |  ||< q|S )z1Get a particular solution from A which is in RREFr   N)rL   r4   )ro   r   r   r  r1   r2   Ainr   r   r   r    s   r  c                 C   s  |j }|j}|dkrd|iS |dkr'd|i}| di d| }r'| |d< |j i i ttf\}}}	}
|  D ]0\}}| D ]'\}}|rS|rS||
|d  |d < q@|r\||	|d < q@|re|||d < q@|}q@q8|	}t||	|}|| | g}td|d D ]}t|
||}|s nt|||}|	|  q|r|d s|
  |r|d rt|
|d |}|ddd }i }tt|tt|t| |d D ]}tt||t| d }t||| }r|||< q|S )a\  
    Berkowitz algorithm for computing the characteristic polynomial.

    Explanation
    ===========

    The Berkowitz algorithm is a division-free algorithm for computing the
    characteristic polynomial of a matrix over any commutative ring using only
    arithmetic in the coefficient ring. This implementation is for sparse
    matrices represented in a dict-of-dicts format (like :class:`SDM`).

    Examples
    ========

    >>> from sympy import Matrix
    >>> from sympy.polys.matrices.sdm import sdm_berk
    >>> from sympy.polys.domains import ZZ
    >>> M = {0: {0: ZZ(1), 1:ZZ(2)}, 1: {0:ZZ(3), 1:ZZ(4)}}
    >>> sdm_berk(M, 2, ZZ)
    {0: 1, 1: -5, 2: -2}
    >>> Matrix([[1, 2], [3, 4]]).charpoly()
    PurePoly(lambda**2 - 5*lambda - 2, lambda, domain='ZZ')

    See Also
    ========

    sympy.polys.matrices.domainmatrix.DomainMatrix.charpoly
        The high-level interface to this function.
    sympy.polys.matrices.dense.ddm_berk
        The dense version of this function.

    References
    ==========

    .. [1] https://en.wikipedia.org/wiki/Samuelson%E2%80%93Berkowitz_algorithm
    r   r   r  r   N)r/   r   r4   r   rg   r@   r=  r?   r>  rM   rA  r  rI   rJ   rB   rL   )r   r    r   r/   r   r  M00r   Rr   ro   r1   r;  r2   r<  AnCRCTvalsRAnCqTqTiTqir   r   r   r    sP   %
(r  N)&r)  operatorr   r   r   r   r   collectionsr   sympy.external.gmpyr   sympy.utilities.decoratorr	   sympy.utilities.iterablesr
   
exceptionsr   r   r   sympy.polys.domainsr   rt   r   __doctest_skip__rg   r   r   r   r   r=  r>  r   r@  r   r   r   r  r  r   r   r   r   <module>   sJ              K.	,> A  
