
    bid=                      d Z ddlmZ ddlmZmZ ddlmZ ddlm	Z	 ddl
mZmZ ddl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 	  G d de	      Ze G d d             Z G d de      Z G d de      Z G d de      Z  G d de      Z! G d de      Z" G d de      Z# G d de#e      Z$ G d de$      Z% G d d e$      Z&y)!a1  
Copyright 2022, the CVXPY authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
    )annotations)ABCabstractmethod)	dataclass)Enum)AnyCallableNconvolve)LinOp)NUMPY_CANON_BACKENDRUST_CANON_BACKENDSCIPY_CANON_BACKENDc                      e Zd ZdZy)ConstantN)__name__
__module____qualname__ID     V/home/cdr/jupyterlab/.venv/lib/python3.12/site-packages/cvxpy/lin_ops/canon_backend.pyr   r   )   s    	Br   r   c                      e Zd ZU dZded<   ded<   ded<   ded<   ded<   d	 Zedd
       ZddZddZ	edd       Z
ddZddZy)TensorRepresentationz
    Sparse representation of a 3D Tensor. Semantically similar to COO format, with one extra
    dimension. Here, 'row' is axis 0, 'col' axis 1, and 'parameter_offset' axis 2.
    
np.ndarraydatarowcolparameter_offsettuple[int, int]shapec                    | j                   j                  | j                  j                  cxk(  r4| j                  j                  cxk(  r| j                  j                  k(  sJ  J y N)r   r"   r   r   r    selfs    r   __post_init__z"TensorRepresentation.__post_init__9   sD    yy$((..aDHHNNadF[F[FaFaaaaaar   c                   t        j                  g       t        j                  g       t        j                  g       t        j                  g       f\  }}}}D ]  }t        j                  ||j                        }t        j                  ||j                        }t        j                  ||j
                        }t        j                  ||j                        } t        fdD              sJ  | ||||d   j                        S )zz
        Concatenates the row, col, parameter_offset, and data fields of a list of
        TensorRepresentations.
        c              3  V   K   | ]   }|j                   d    j                   k(   " yw)r   Nr"   ).0ttensorss     r   	<genexpr>z/TensorRepresentation.combine.<locals>.<genexpr>J   s$     @1177gaj...@s   &)r   )	nparrayappendr   r   r   r    allr"   )clsr-   r   r   r   r    r,   s    `     r   combinezTensorRepresentation.combine<   s     ,.88B<"rxxPR|UWU]U]^`Ua+a(c3(  	OA99T166*D))C'C))C'C!yy)91;M;MN		O
 @@@@@4c#3WQZ5E5EFFr   c                   t        |t              xr t        j                  | j                  |j                  k(        xr t        j                  | j
                  |j
                  k(        xru t        j                  | j                  |j                  k(        xrG t        j                  | j                  |j                  k(        xr | j                  |j                  k(  S r$   )	
isinstancer   r/   r2   r   r   r   r    r"   r&   others     r   __eq__zTensorRepresentation.__eq__M   s    %!56 &FF499

*+&FF488uyy()& FF488uyy()& FF4((E,B,BBC	&
 JJ%++%	&r   c           	        | j                   |j                   k7  rt        d      t        t        j                  | j
                  |j
                  g      t        j                  | j                  |j                  g      t        j                  | j                  |j                  g      t        j                  | j                  |j                  g      | j                         S )NzShapes must match for addition.)	r"   
ValueErrorr   r/   concatenater   r   r   r    r7   s     r   __add__zTensorRepresentation.__add__U   s    ::$>??#NNDIIuzz23NNDHHeii01NNDHHeii01NND1153I3IJKJJ
 	
r   c           	          | t        j                  g t              t        j                  g t              t        j                  g t              t        j                  g t              |      S )Ndtype)r/   r0   floatint)r3   r"   s     r   empty_with_shapez%TensorRepresentation.empty_with_shape`   sJ    HHRu%HHRs#HHRs#HHRs#
 	
r   c                   | j                   j                  t        j                        t        j                  | j                  d         z  | j
                  j                  t        j                        z   }| j                  j                  t        j                        }t        j                  | j                  t        j                        |f}t        j                  | j                  ||ff|      S )zW
        Flatten into 2D scipy csc-matrix in column-major order and transpose.
        r   r?   r*   )r   astyper/   int64r"   r   r    prodsp	csc_arrayr   )r&   num_param_slicesrowscolsr"   s        r   flatten_tensorz#TensorRepresentation.flatten_tensorj   s     )BHHTZZ],CCdhhooVXV^V^F__$$++BHH528846FG||TYYt5UCCr   c                    | j                   |k(  }t        j                  | j                  |   | j                  |   | j
                  |   ff| j                        S )zT
        Returns a single slice of the tensor for a given parameter offset.
        )r    rH   rI   r   r   r   r"   )r&   param_offsetmasks      r   get_param_slicez$TensorRepresentation.get_param_slices   sN     $$4||TYYt_txx~txx~.NOQUQ[Q[\\r   Nr-   zlist[TensorRepresentation]returnr   )r8   r   rS   bool)r8   r   rS   r   )r"   r!   rS   r   )rJ   rB   rS   sp.csc_array)rO   rB   rS   rU   )r   r   r   __doc____annotations__r'   classmethodr4   r9   r=   rC   rM   rQ   r   r   r   r   r   -   sk     	O	O  b G G &	
 
 
D]r   r   c                  D    e Zd Z	 	 	 	 	 	 ddZedd       Zedd       Zy)CanonBackendc                J    || _         || _        || _        || _        || _        y)aq  
        CanonBackend handles the compilation from LinOp trees to a final sparse tensor through its
        subclasses.

        Parameters
        ----------
        id_to_col: mapping of variable id to column offset in A.
        param_to_size: mapping of parameter id to the corresponding number of elements.
        param_to_col: mapping of parameter id to the offset in axis 2.
        param_size_plus_one: integer representing shape[2], i.e., the number of slices along axis 2
                             plus_one refers to the non-parametrized slice of the tensor.
        var_length: number of columns in A.
        N)param_size_plus_one	id_to_colparam_to_sizeparam_to_col
var_length)r&   r]   r^   r_   r\   r`   s         r   __init__zCanonBackend.__init__|   s*     $7 "*($r   c                X    t         t        t        t        t        t
        i} ||   |i |S )aO  
        Map the name of a subclass and its initializing arguments to an instance of the subclass.

        Parameters
        ----------
        backend_name: key pointing to the subclass.
        args: Arguments required to initialize the subclass.

        Returns
        -------
        Initialized CanonBackend subclass.
        )r   NumPyCanonBackendr   SciPyCanonBackendr   RustCanonBackend)r3   backend_nameargskwargsbackendss        r   get_backendzCanonBackend.get_backend   s6      !2!2 0

 &x%t6v66r   c                     y)a  
        Main function called from canonInterface.
        Given a list of LinOp trees, each representing a constraint (or the objective), get the
        [A b] Tensor for each, stack them and return the result reshaped as a 2D sp.csc_array
        of shape (total_rows * (var_length + 1)), param_size_plus_one)

        Parameters
        ----------
        lin_ops: list of linOp trees.

        Returns
        -------
        2D sp.csc_array representing the constraints (or the objective).
        Nr   )r&   lin_opss     r   build_matrixzCanonBackend.build_matrix   s      	r   N)
r]   dict[int, int]r^   rn   r_   rn   r\   rB   r`   rB   )rf   strrS   rZ   rl   zlist[LinOp]rS   rU   )r   r   r   ra   rX   rj   r   rm   r   r   r   rZ   rZ   {   sG    %-%DG%UX%* 7 7(  r   rZ   c                     e Zd ZdZd&dZd'dZ	 	 d(dZeed)d              Z	eed*d              Z
ed+d       Zed,d       Zd-d	Zed.d
       Zed.d       Zed/d       Zeed/d              Zeed/d              Zed.d       Zed/d       Zeed.d              Zed/d       Zed/d       Zeed/d              Zeed0d              Zd/dZd/dZd/dZed/d       Zed/d       Zed/d       Zed/d       Z eed/d              Z!ed/d       Z"ed/d       Z#ed/d        Z$ed!        Z%ed1d"       Z&ed2d#       Z'ed3d$       Z(y%)4PythonCanonBackendaB  
    Each tensor has 3 dimensions. The first one is the parameter axis, the second one is the rows
    and the third one is the variable columns.

    For example:
    - A new variable of size n has shape (1, n, n)
    - A new parameter of size n has shape (n, n, 1)
    - A new constant of size n has shape (1, n, 1)
    c                   | j                   | j                  d<   g }t        d |D              }d}|D ]i  }t        j                  |j
                        }| j                         }| j                  ||      }|j                  |j                  ||             ||z  }k | j                  |      }	| j                  j                  d       |	j                  | j                        S )Nr   c              3  Z   K   | ]#  }t        j                  |j                         % y wr$   r/   rG   r"   )r+   lin_ops     r   r.   z2PythonCanonBackend.build_matrix.<locals>.<genexpr>   s     E6.E   )+r   )r`   r]   sumr/   rG   r"   get_empty_viewprocess_constraintr1   get_tensor_representationconcatenate_tensorspoprM   r\   )
r&   rl   constraint_res
total_rows
row_offsetrv   lin_op_rows
empty_viewlin_op_tensor
tensor_ress
             r   rm   zPythonCanonBackend.build_matrix   s    !__rEWEE

 	&F''&,,/K,,.J 33FJGM!!-"I"I*V`"ab+%J	& --n=
2(()A)ABBr   c                   |j                   dk(  rt        |j                  t              sJ t        j
                  st        |j                        dv sJ | j                  |j                  |j                        }|j                  |j                  h|d      S |j                   dv rH| j                  |j                        }|j                  t        j                  j                  h|d      S |j                   dk(  rS| j                  |j                  |j                        }|j                  t        j                  j                  h|d      S | j                  |j                         }|j                   dv r	 |||      S d	}|j                   D ]'  }| j#                  ||      }	 |||	      }
||
}#||
z  }) |J |S )
a  
        Depth-first parsing of a linOp node.

        Parameters
        ----------
        lin_op: a node in the linOp tree.
        empty_view: TensorView used to create tensors for leaf nodes.

        Returns
        -------
        The processed node as a TensorView.
        variable>   r         T)is_parameter_free>   dense_constscalar_constsparse_constparamF>   hstackvstackr<   N)typer6   r   rB   sALLOW_ND_EXPRlenr"   get_variable_tensorcreate_new_tensor_viewget_data_tensorr   r   valueget_param_tensorget_funcrg   rz   )r&   rv   r   variable_tensordata_tensorparam_tensorfuncresarg	arg_coeffarg_ress              r   rz   z%PythonCanonBackend.process_constraint   s    ;;*$fkk3///??c&,,&79&DDD"66v||V[[QO44fkk]OGK 5 M M[[KK..v{{;K44hkk6G6G5H+GK 5 M M[[G#00v{{KL44hkk6G6G5H,GL 5 N N
 ==-D{{AAFJ//C{{ # 33CD	vy1;!C7NC# ?"?Jr   c                ~   h d}|s;|j                   |v r-t        |j                        dk(  r| j                  |      }|dfS | j	                  ||      }|j
                  t        j                  j                  hk(  sJ |j                  t        j                  j                     }|s_t        |j                        dk\  rGt        |j                        dk(  r|j                  nd|j                  d   g}| j                  ||      }|j                  r|t        j                  j                     n|}||j                  fS )z
        Extract the constant data from a LinOp node. In most cases, lin_op will be of
        type "*_const" or "param", but can handle arbitrary types.
        >   r   r   r   r   Tr   r   )r   r   r"   get_constant_data_from_constrz   variable_idsr   r   r   tensorreshape_constant_datar   )	r&   rv   viewcolumn	constantsconstant_dataconstant_viewlin_op_shapedata_to_returns	            r   get_constant_dataz$PythonCanonBackend.get_constant_data  s    D	&++2s6<<7HA7M ==fEM $&&//=))hkk.?.?-@@@@%,,X[[->->?#fll+q0 ,/v||+<+A6<<6<<XY?G[L 66}lSM=J=\=\x{{'8'89 	}>>>>r   c                     y)P
        Extract the constant data from a LinOp node of type "*_const".
        Nr   )rv   s    r   r   z/PythonCanonBackend.get_constant_data_from_const       	r   c                     y)z
        Reshape constant data from column format to the required shape for operations that
        do not require column format
        Nr   )r   r   s     r   r   z(PythonCanonBackend.reshape_constant_data&       	r   c                ,    t         j                  |       S z
        Takes list of tensors which have already been offset along axis 0 (rows) and
        combines them into a single tensor.
        r   r4   )r-   s    r   r|   z&PythonCanonBackend.concatenate_tensors/       $++G44r   c                     y)z
        Returns an empty view of the corresponding TensorView subclass, coupling the CanonBackend
        subclass with the TensorView subclass.
        Nr   r%   s    r   ry   z!PythonCanonBackend.get_empty_view7  r   r   c                >   i d| j                   d| j                  d| j                  d| j                  d| j                  d| j
                  d| j                  d| j                  d	| j                  d
| j                  d| j                  d| j                  d| j                  d| j                  d| j                  d| j                  d| j                   | j"                  | j$                  | j&                  | j(                  | j*                  d}||   S )z
        Map the name of a function as given by the linOp to the implementation.

        Parameters
        ----------
        func_name: The name of the function.

        Returns
        -------
        The function implementation.
        rx   mulpromotebroadcast_tonegmul_elemsum_entriesdivreshapeindexdiag_vecr   r   r<   	transpose	upper_tridiag_mat)rmultraceconvkron_lkron_r)sum_opr   r   r   r   r   r   r   r   r   r   r   r   r<   r   r   r   r   r   r   r   r   )r&   	func_namemappings      r   r   zPythonCanonBackend.get_func?  sR   
4;;
488
 t||
 D--	

 488
 
 4++
 488
 t||
 TZZ
 
 dkk
 dkk
 4++
 
  !
" #
$ IIZZIIkkkk-
0 y!!r   c                    |S )zJ
        Sum (along axis 1) is implicit in Ax+b, so it is a NOOP.
        r   _linr   s     r   r   zPythonCanonBackend.sum_ope  	    
 r   c                    |S )z[
        Reshaping only changes the shape attribute of the LinOp, so it is a NOOP.
        r   r   s     r   r   zPythonCanonBackend.reshapel  r   r   c                     y)  
        Multiply view with constant data from the left.
        When the lhs is parametrized, multiply each slice of the tensor with the
        single, constant slice of the rhs.
        Otherwise, multiply the single slice of the tensor with each slice of the rhs.
        Nr   r&   linr   s      r   r   zPythonCanonBackend.muls       	r   c                     y)z?
        Promote view by repeating along axis 0 (rows)
        Nr   r   r   s     r   r   zPythonCanonBackend.promote}  r   r   c                     y)z0
        Broadcast view to a new shape.
        Nr   r   s     r   r   zPythonCanonBackend.broadcast_to  r   r   c                .    d }|j                  |       |S )8
        Given (A, b) in view, return (-A, -b).
        c                    |  S r$   r   )xs    r   r   z$PythonCanonBackend.neg.<locals>.func  	    2Ir   	apply_allr   r   r   s      r   r   zPythonCanonBackend.neg  s    	 	tr   c                     y)o  
        Given (A, b) in view and constant data d, return (A*d, b*d).
        d is broadcasted along dimension 1 (columns).
        When the lhs is parametrized, multiply elementwise each slice of the tensor with the
        single, constant slice of the rhs.
        Otherwise, multiply elementwise the single slice of the tensor with each slice of the rhs.
        Nr   r   s      r   r   zPythonCanonBackend.mul_elem       	r   c                     y)zN
        Given (A, b) in view, return (sum(A,axis=0), sum(b, axis=0))
        Nr   r   s     r   r   zPythonCanonBackend.sum_entries  r   r   c                     y)aU  
        Given (A, b) in view and constant data d, return (A*(1/d), b*(1/d)).
        d is broadcasted along dimension 1 (columns)
        This function is semantically identical to mul_elem but the view x
        is multiplied with the reciprocal of the lin_op data instead.

        Note: div currently doesn't support parameters.
        Nr   r   s      r   r   zPythonCanonBackend.div  s     	r   c                   | j                   D cg c]7  }t        j                  |j                  |j                  |j
                        9 }}t        |      dkD  sJ |d   }t        j                  | j                  d   j                  g      }t        dt        |            D ]B  }||dz
     }t        j                  j                  |||   |z        j                  d      }|}D |j                  |       |S c c}w )z
        Given (A, b) in view, select the rows corresponding to the elements of the expression being
        indexed. Supports an arbitrary number of dimensions.
        r   r   Forder)r   r/   arangestartstopstepr   cumprodrg   r"   rangeaddouterflattenselect_rows)	r   r   r   indicesrK   cum_prodiproduct_sizeoffsets	            r   r   zPythonCanonBackend.index  s     @CxxH!299QWWaffaff5HH7|aqz::sxx{0012q#g,' 	A#AE?LVV\\$
\(ABJJQTJUFD		
 	 Is   <C<c                     y)e  
        Diagonal vector to matrix. Given (A, b) with n rows in view, add rows of zeros such that
        the original rows now correspond to the diagonal entries of the n x n expression
        An optional offset parameter `k` can be specified, with k>0 for diagonals above
        the main diagonal, and k<0 for diagonals below the main diagonal.
        Nr   r   s     r   r   zPythonCanonBackend.diag_vec  r   r   c                     y)
        Returns a function that takes in a tensor, modifies the shape of the tensor by extending
        it to total_rows, and then shifts the entries by offset along axis 0.
        Nr   )r   r   s     r   get_stack_funcz!PythonCanonBackend.get_stack_func  r   r   c                6   d}t        d |j                  D              }d}|j                  D ]e  }| j                  ||      }| j                  ||      }|j	                  |       t        j                  |j                        }	||	z  }||}a||z  }g |J |S )z
        Given views (A0,b0), (A1,b1),..., (An,bn), stack all tensors along axis 0,
        i.e., return
        (A0, b0)
         A1, b1
         ...
         An, bn.
        r   c              3  Z   K   | ]#  }t        j                  |j                         % y wr$   ru   r+   r   s     r   r.   z,PythonCanonBackend.hstack.<locals>.<genexpr>  s     @+@rw   N)rx   rg   rz   r   r   r/   rG   r"   )
r&   r   r   r   r   r   r   arg_viewr   arg_rowss
             r   r   zPythonCanonBackend.hstack  s     @sxx@@
88 		 C..sD9H&&z6:Dt$wwsyy)HhF{x		  
r   c                ^   | j                  ||      }|j                  d   }|Bt        j                  t	        d |j
                  D                    }|j                  |       |S d}g }|j
                  D ]h  }t        j                  |j                        }	|j                  t        j                  |	      j                  |j                  d      |z          ||	z  }j t        j                  ||      j                  d      j                  t              }|j                  |       |S )aZ  Concatenate multiple tensors along a specified axis.

        This method performs the concatenation of multiple tensors, following NumPy's behavior.
        It correctly maps the indices from the input tensors to the concatenated output tensor,
        ensuring that elements are placed in the correct positions in the resulting tensor.
        r   r   c              3  Z   K   | ]#  }t        j                  |j                         % y wr$   ru   r  s     r   r.   z1PythonCanonBackend.concatenate.<locals>.<genexpr>  s     !I"''#))"4!Irw   r   r   axis)r   r   r/   r   rx   rg   r   rG   r"   r1   r   r<   r   rE   rB   )
r&   r   r   r   r	  r   r   r   r   r  s
             r   r<   zPythonCanonBackend.concatenate  s     kkck-xx{<IIc!I!IIJEOOE"J88 	Cwwsyy)HNN299X.66syy#6NQWWXhF	 wt4<<3<GNNsS
r   c                   | j                  ||      }d}g }|j                  D ]h  }t        j                  |j                        }|j                  t        j                  |      j                  |j                  d      |z          ||z  }j t        j                  |      j                  d      j                  t              }|j                  |       |S )z
        Given views (A0,b0), (A1,b1),..., (An,bn), first, stack them along axis 0 via hstack.
        Then, permute the rows of the resulting tensor to be consistent with stacking the arguments
        vertically instead of horizontally.
        r   r   r   r   )r   rg   r/   rG   r"   r1   r   r   r   r   rE   rB   r   )	r&   r   r   r   r   r   r   r  r   s	            r   r   zPythonCanonBackend.vstack  s     kkck-88 	Cwwsyy)HNN299X.66syy6LvUVhF	 		'"***5<<SA
r   c                :   | j                   d   }| j                  d   j                  }t        j                  t        j
                  |            j                  |d      }t        j                  ||      }|j                  d      }|j                  |       |S )z
        Given (A, b) in view, permute the rows such that they correspond to the transposed
        expression with arbitrary axis permutation.
        r   r   r   )
r   rg   r"   r/   r   rG   r   r   r   r   )r   r   axesoriginal_shaper   transposed_indicesrK   s          r   r   zPythonCanonBackend.transpose!  s     xx{!**))BGGN34<<^SV<W\\'48!)))4r   c                *   t        j                  t        j                  | j                  d   j                              j                  | j                  d   j                  d      }|t        j                  |d         }|j                  |       |S )z
        Given (A, b) in view, select the rows corresponding to the elements above the diagonal
        in the original expression.
        Note: The diagonal itself is not included.
        r   r   r   r   )k)r/   r   rG   rg   r"   r   triu_indices_fromr   )r   r   r   triu_indicess       r   r   zPythonCanonBackend.upper_tri/  sr     ))BGGCHHQK$5$567??@Q@QY\?]r33GqAB&r   c                l   | j                   d   }| j                  }|t        |      z   }|dk(  rt        j                  |      |dz   z  }nE|dkD  r"t        j                  |      |dz   z  ||z  z   }nt        j                  |      |dz   z  |z
  }|j                  |j                  t                     |S )aF  
        Diagonal matrix to vector. Given (A, b) in view, select the rows corresponding to the
        elements on the diagonal in the original expression.
        An optional offset parameter `k` can be specified, with k>0 for diagonals above
        the main diagonal, and k<0 for diagonals below the main diagonal.
        r   r   )r"   r   absr/   r   r   rE   rB   )r   r   rK   r  original_rowsdiag_indicess         r   r   zPythonCanonBackend.diag_mat;  s     yy|HHs1v699T?dQh7LU99T?ma.?@=STCTTL99T?ma.?@1DL,,S12r   c                     y)a  
        Multiply view with constant data from the right.
        When the rhs is parametrized, multiply each slice of the tensor with the
        single, constant slice of the lhs.
        Otherwise, multiply the single slice of the tensor with each slice of the lhs.
        Nr   r   s      r   r   zPythonCanonBackend.rmulO  r   r   c                     y)w
        Select the rows corresponding to the diagonal entries in the expression and sum along
        axis 0.
        Nr   r   s     r   r   zPythonCanonBackend.traceY  r   r   c                     y)  
        Returns view corresponding to a discrete convolution with data 'a', i.e., multiplying from
        the left a repetition of the column vector of 'a' for each column in A, shifted down one row
        after each column, i.e., a Toeplitz matrix.
        If lin_data is a row vector, we must transform the lhs to become a column vector before
        applying the convolution.

        Note: conv currently doesn't support parameters.
        Nr   r   s      r   r   zPythonCanonBackend.convb  s     	r   c                     y)z
        Returns view corresponding to Kronecker product of data 'a' with view x, i.e., kron(a,x).

        Note: kron_r currently doesn't support parameters.
        Nr   r   s      r   r   zPythonCanonBackend.kron_ro  r   r   c                     y)z
        Returns view corresponding to Kronecker product of view x with data 'a', i.e., kron(x,a).

        Note: kron_l currently doesn't support parameters.
        Nr   r   s      r   r   zPythonCanonBackend.kron_lx  r   r   c                
   t        j                  |      }t        j                  |       }t        j                  t        j                  |            j	                  |d      }t        j                  t        j                  |             j	                  | d      }t        j
                  ||      t        j
                  ||t        j                  |      z        z   j                  d      j                  t              }|S )z
        Internal function that computes the row indices corresponding to the
        kronecker product of two sparse tensors.
        r   r   )	r/   onesr   rG   r   kronr   rE   rB   )	lhs_shape	rhs_shaperhs_oneslhs_ones
rhs_arange
lhs_arangerow_indicess          r   _get_kron_row_indicesz(PythonCanonBackend._get_kron_row_indices  s     779%779%YYrwwy12::9C:P
YYrwwy12::9C:P
wwx4wwz8bggi6H+HIJW3Ws 	 r   c                     y)z
        Returns tensor of a variable node, i.e., eye(n) across axes 0 and 1, where n is
        the size of the variable.
        Nr   )r&   r"   variable_ids      r   r   z&PythonCanonBackend.get_variable_tensor  r   r   c                     y)zE
        Returns tensor of constant node as a column vector.
        Nr   )r&   r   s     r   r   z"PythonCanonBackend.get_data_tensor      
 	r   c                     y)z
        Returns tensor of a parameter node, i.e., eye(n) across axes 0 and 2, where n is
        the size of the parameter.
        Nr   )r&   r"   parameter_ids      r   r   z#PythonCanonBackend.get_param_tensor  r   r   Nrp   )rv   r   r   
TensorViewrS   r/  )rv   r   r   r/  r   rT   rS   z%tuple[np.ndarray | sp.spmatrix, bool])rv   r   rS   np.ndarray | sp.spmatrix)r   r   r   r!   rS   r   rR   )rS   r/  )r   ro   rS   r	   )r   r   r   r/  rS   r/  )r   r   r   r/  rS   r/  r   rB   r   rB   rS   r	   )r"   tuple[int, ...]r*  rB   rS   r   )r   r   rS   r   )r"   r2  r.  rB   rS   r   ))r   r   r   rV   rm   rz   r   staticmethodr   r   r   r|   ry   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   rr   rr      s   C"-^?4?4       5 5  $"L             	 	      	 	  "      22"   	 	  &      
 
             r   rr   c                      e Zd ZdZddZy)re   z
    The rust canonicalization backend is currently WIP and cannot be used.
    For additional information, a proof of concept pull request can be found here:
    https://github.com/phschiele/cvxpy/pull/31
    c                D   dd l }| j                  | j                  d<   |j                  || j                  | j                  | j
                  | j                  | j                        \  }\  }}}| j                  j                  d       t        j                  |||ff|      S )Nr   r   )

cvxpy_rustr`   r]   rm   r\   r^   r_   r}   rH   rI   )r&   rl   r6  r   r   r   r"   s          r   rm   zRustCanonBackend.build_matrix  s    !__r$.$;$;G<@<T<T<@NN<@<N<N<@<M<M<@OO%M!zS5 	2||TC:.66r   Nrp   )r   r   r   rV   rm   r   r   r   re   re     s    
7r   re   c                      e Zd Zedd       Ze	 	 	 	 dd       Z	 	 ddZddZddZedd       Z	edd       Z
ddZedd	       Zdd
Zedd       Zedd       ZddZedd       ZddZddZddZ	 	 ddZddZ	 	 d dZed        Zy)!rc   c                z    t         j                  | j                        }|j                  | j                  k(  sJ |S r   )rc   	_to_denser   r"   )rv   constants     r   r   z.NumPyCanonBackend.get_constant_data_from_const  s3    
 %..v{{;~~---r   c           	         | j                         D ci c]*  \  }}||j                  |j                  d   g|d      , c}}S c c}}w )a  
        Reshape constant data from column format to the required shape for operations that
        do not require column format. This function unpacks the constant data dict and reshapes
        dimensions 1 and 2 of the tensor 'v' according to the lin_op_shape argument.
        r   r   r   )itemsr   r"   r   r   r  vs       r   r   z'NumPyCanonBackend.reshape_constant_data  sS     *//13Aq 199aggaj8<89DD 3 	3 3s   /Ac                ,    t         j                  |      S r   r   )r&   r-   s     r   r|   z%NumPyCanonBackend.concatenate_tensors  r   r   c                    t         j                  | j                  | j                  | j                  | j
                  | j                        S )z
        Returns an empty view of the corresponding NumPyTensorView subclass,
        coupling the NumPyCanonBackend subclass with the NumPyTensorView subclass.
        )NumPyTensorViewry   r\   r]   r^   r_   r`   r%   s    r   ry   z NumPyCanonBackend.get_empty_view  s=    
 --d.F.F.2.@.@$BSBS.2oo? 	?r   c           
       
 | j                  |j                  |d      \  }}t        |t              r|j                  t        t        |j                                     d   j                  d   z  }|j                         D ci c]/  \  }}|t        j                  t        j                  |      |      1 c}}

fd}|}	nft        |t        j                        sJ |j                  |j                  d   z  }t        j                  t        j                  |      |      

fd}	|j                  |	|      S c c}}w )r   Fr   r   r   c                    | j                   d   dk(  sJ j                         D ci c]  \  }}||| z   c}}S c c}}w Nr   r   r"   r=  r   r  r?  stacked_lhss      r   parametrized_mulz/NumPyCanonBackend.mul.<locals>.parametrized_mul  @    wwqzQ&-8->->-@ATQ1q5AAA   =c                    | z  S r$   r   r   rI  s    r   r   z#NumPyCanonBackend.mul.<locals>.func      "Q&r   is_param_free_function)r   r   r6   dictrK   nextitervaluesr"   r=  r/   r   eyendarrayaccumulate_over_variables)r&   r   r   lhsis_param_free_lhsrepsr  r?  rJ  r   rI  s             @r   r   zNumPyCanonBackend.mul  s    "&!7!7$u!7!Uc4 99T#**,%7 8 ; A A" EEDCF99;O41a1bggbffTlA66OKB $Dc2::...99		"-D''"&&,4K' --dK\-]] Ps   4D>c                    t        t        j                  | j                              fd}|j	                  |       |S )z@
        Promote view by repeating along axis 1 (rows).
        c                6    t        j                  | ddf      S Nr   )r/   tile)r   num_entriess    r   r   z'NumPyCanonBackend.promote.<locals>.func  s    771q+q122r   )rB   r/   rG   r"   r   )r   r   r   r`  s      @r   r   zNumPyCanonBackend.promote  s4    
 "''#)),-	3 	tr   c                <   | j                   }| j                  d   j                   }t        j                  t        j                  |t
                    j                  |d      }t        j                  ||      j                  d      }|j                  |       |S z^
        Broadcast view by calling np.broadcast_to on the rows and indexing the view.
        r   r?   r   r   
r"   rg   r/   r   rG   rB   r   r   r   r   r   r   broadcast_shaper  rK   s        r   r   zNumPyCanonBackend.broadcast_to  {    
 ))!**yys;<DD^[^D_t_5==C=Hr   c                    | j                  |j                  |d      \  }t        t              rfd}|}nfd}|j	                  ||      S )r   TrD  c                    | j                   d   dk(  sJ j                         D ci c]  \  }}||| z   c}}S c c}}w rF  rG  )r   r  r?  rY  s      r   rJ  z4NumPyCanonBackend.mul_elem.<locals>.parametrized_mul"  s=    wwqzQ&-0YY[9TQ1q5999rL  c                    | z  S r$   r   r   rY  s    r   r   z(NumPyCanonBackend.mul_elem.<locals>.func(  s    Qwr   rP  )r   r   r6   rR  rX  )r&   r   r   rZ  rJ  r   rY  s         @r   r   zNumPyCanonBackend.mul_elem  sX     "&!7!7$t!7!Tc4 : $D--dK\-]]r   c                4      fd}|j                  |       |S )a  
        Given (A, b) in view, return the sum of the representation
        on the row axis, ie: (sum(A, axis=axis), sum(b, axis=axis)).

        Note for new n-dimensional version: We now pass an axis parameter to the sum.
        The new implementation keeps the columns of the tensor fixed and reshapes the
        remaining dimensions in the original shape of the expression. The sum is then
        performed along the axis parameter. Finally, the tensor is reshaped back to the
        desired output shape.

        Example:
        # Suppose we want to sum a Variable(2,2,2)
        x = np.eye(8)
        out = x.reshape(2,2,2,8).sum(axis=axis).reshape(n // prod(shape[axis]),8)
        c                   	j                   \  }}|| j                  dd      S 	j                  d   j                  }| j                  d   }| j                  d   }t	        |t
              rIt        j                  |D cg c]  }||   	 c}t              }t        |D cg c]  }|dz   	 c}      }n
||   }|dz  }| j                  |f|z   |fz   d      j                  |	      } | j                  |||z  |fd      S c c}w c c}w )
Nr   T)r	  keepdimsr   r   r?   r   r   r  )
r   rx   rg   r"   r6   tupler/   rG   rB   r   )
r   r	  _r"   npr   dar   s
            r   r   z+NumPyCanonBackend.sum_entries.<locals>.func=  s    iiGD!|uu!du33		!**GGBKGGAJdE*4 8aq 8DA !6A!a%!67DdAAIDIIqd5j!oSI9==4=Hyy!QT1Sy99 !9!6s    D"D	r   r   s   `  r   r   zNumPyCanonBackend.sum_entries,  s    "	:$ 	tr   c                    | j                  |j                  |d      \  }|sJ j                  d   dk(  sJ t        j                  dk7  t
              fd}|j                  ||      S )N  
        Given (A, b) in view and constant data d, return (A*(1/d), b*(1/d)).
        d is broadcasted along dimension 1 (columns).
        This function is semantically identical to mul_elem but the view x
        is multiplied with the reciprocal of the lin_op data.

        Note: div currently doesn't support parameters.
        TrD  r   r   )wherer@   c                    | z  S r$   r   rj  s    r   div_funcz'NumPyCanonBackend.div.<locals>.div_funca      7Nr   rP  )r   r   r"   r/   
reciprocalrA   rX  r&   r   r   rZ  rx  rY  s        @r   r   zNumPyCanonBackend.divR  sy     "&!7!7$t!7!T   yy|q   mmCsaxu=	 --hO`-aar   c                    | j                   d   | j                   d   k(  sJ | j                  | j                   d   t        | j                   d   dz        fd}|j                  |       |S )af  
        Diagonal vector to matrix. Given (A, b) with n rows in view, add rows of zeros such that
        the original rows now correspond to the diagonal entries of the n x n expression.
        An optional offset parameter `k` can be specified, with k>0 for diagonals above
        the main diagonal, and k<0 for diagonals below the main diagonal.
        r   r   r   c                h   | j                   d   }t        | j                         }|d<   dk(  rt        j                  |      dz   z  }nEdkD  r"t        j                  |      dz   z  z  z   }nt        j                  |      dz   z  z
  }t        j                  |      }| |d d |d d f<   |S Nr   r   )r"   listr/   r   zeros)r   x_rowsr"   new_rowsmatrixr  rK   r   s        r   r   z(NumPyCanonBackend.diag_vec.<locals>.funcs  s    WWQZFME!E!HAv99V,q9Q99V,q9D1HD99V,q9A=XXe_F%&F1h>"Mr   r"   r   rB   r   r   r   r   r  rK   r   s      @@@r   r   zNumPyCanonBackend.diag_vecf  sh     yy|syy|+++HHyy|1*+
	 	tr   c                      fd}|S )z
        Returns a function that takes in a tensor, modifies the shape of the tensor by extending
        it to total_rows, and then shifts the entries by offset along axis 1.
        c                   | j                   d   }t        j                  |      z   j                  t              }t        j
                  | j                   d   t	              | j                   d   f      }| |d d |d d f<   |S )Nr   r   r   r*   )r"   r/   r   rE   rB   r  )r   rK   r  r  r   r   s       r   
stack_funcz4NumPyCanonBackend.get_stack_func.<locals>.stack_func  sl    <<?D		$&088=HXXV\\!_c*ov||TU$WXF%+F1h>"Mr   r   r   r   r  s   `` r   r   z NumPyCanonBackend.get_stack_func  s    	 r   c                    | j                  |j                  |d      \  }}t        |j                  d   j                        dk(  r|j                  d   j                  d   n|j                  d   j                  d   }|r|j                  d   }t        |j                  j                        dk(  r||k7  rt        j                  |dd      }|j                  d   }|j                  |z  }t        j                  |dd      }t        j                  |t        j                  |            fd}	n=t        t        |j                                     d   j                  }
|
d   }t        |j                  j                        dk(  rl||k7  rg|j                         D ci c]  \  }}|t        j                  |dd       }}}t        t        |j                                     d   j                  }
|
d   }|j                  |z  }|j                         D ci c]D  \  }}|t        j                  t        j                  |dd      t        j                  |            F c}}fd}|}	|j                  |	|	      S c c}}w c c}}w )
  
        Multiply view with constant data from the right.
        When the rhs is parametrized, multiply each slice of the tensor with the
        single, constant slice of the lhs.
        Otherwise, multiply the single slice of the tensor with each slice of the lhs.

        Note: Even though this is rmul, we still use "lhs", as is implemented via a
        multiplication from the left in this function.
        FrD  r   r   r   c                    | z  S r$   r   rN  s    r   r   z$NumPyCanonBackend.rmul.<locals>.func  rO  r   c                    | j                   d   dk(  sJ j                         D ci c]  \  }}||| z   c}}S c c}}w rF  rG  rH  s      r   rJ  z0NumPyCanonBackend.rmul.<locals>.parametrized_mul  rK  rL  rP  )r   r   r   rg   r"   r/   swapaxesrK   r   rV  rS  rT  rU  r=  rX  )r&   r   r   rY  rZ  arg_colslhs_rowsr[  lhs_transposedr   r!  r  r?  rJ  rI  s                 @r   r   zNumPyCanonBackend.rmul  s    "&!7!7$u!7!U+.sxx{/@/@+AQ+F388A;$$Q'CHHUVKL]L]^_L`yy}H388>>"a'H,@kk#r2.yy}H99(D[[b"5N''."&&,?K' T#**,/0399I }H388>>"a'H,@=@YY[ITQq"++aR00II cjjl!34Q7==	 }H99(DX[XaXaXcdPTPQST1bggbkk!R&<bffTlKKdKB $D--dK\-]] J es   ""JA	J
c                8   | j                   d   j                  }t        j                  |d         |d   z  t        j                  |d         z   }t        j                  dt        j
                  |      f      dd|f<   fd}|j                  |d      S )r  r   r   r*   c                    | z  S r$   r   rj  s    r   r   z%NumPyCanonBackend.trace.<locals>.func  ry  r   TrP  )rg   r"   r/   r   r  rG   rX  )r   r   r"   r   r   rY  s        @r   r   zNumPyCanonBackend.trace  s     !!))E!H%a0299U1X3FFhha01AwJ	 --d4-PPr   c                    | j                  |j                  |d      \  }|sJ t        |j                  j                        dk(  rt	        j
                  dd      fd}|j                  ||      S )r  FrD  r   r  r   c                    t        |       S r$   r
   rj  s    r   r   z$NumPyCanonBackend.conv.<locals>.func  s    C##r   rP  )r   r   r   r"   r/   r  rX  )r&   r   r   rZ  r   rY  s        @r   r   zNumPyCanonBackend.conv  st     "&!7!7$u!7!U   sxx~~!#++c2r*C	$ --dK\-]]r   c                   | j                  |j                  |d      \  }|sJ t              dk(  sJ d   j                  dk(  sJ t        |j                  D ch c]  }|j
                   c}      dk(  sJ |j                  d   j
                  }| j                  |j                  j
                  |      dfd}|j                  ||      S c c}w )	C  
        Returns view corresponding to Kronecker product of data 'a' with view x, i.e., kron(a,x).
        This function reshapes 'a' into a column vector, computes the Kronecker product with the
        view of x and reorders the row indices afterwards.

        Note: kron_r currently doesn't support parameters.
        TrD  r   r   r   c                n    | j                   dk(  sJ t        j                  |       }|d d d d f   }|S N   ndimr/   r   )r   kron_resrY  row_idxs     r   r   z&NumPyCanonBackend.kron_r.<locals>.func  s7    66Q;;wwsAH7A.HOr   rP  r   r   rS   r   r   r   r   r  rg   r"   r(  rX  	r&   r   r   rZ  r   r"  r   rY  r  s	          @@r   r   zNumPyCanonBackend.kron_r  s     "&!7!7$t!7!T   3x1}}!fxx1}}2#CII23q888HHQK%%	,,SXX^^YG	 --dK\-]] 3   !Cc                   | j                  |j                  |d      \  }|sJ t              dk(  sJ d   j                  dk(  sJ t        |j                  D ch c]  }|j
                   c}      dk(  sJ |j                  d   j
                  }| j                  ||j                  j
                        dfd}|j                  ||      S c c}w )	C  
        Returns view corresponding to Kronecker product of view x with data 'a', i.e., kron(x,a).
        This function reshapes 'a' into a column vector, computes the Kronecker product with the
        view of x and reorders the row indices afterwards.

        Note: kron_l currently doesn't support parameters.
        TrD  r   r   r   c                n    | j                   dk(  sJ t        j                  |       }|d d d d f   }|S r  r  )r   r  rhsr  s     r   r   z&NumPyCanonBackend.kron_l.<locals>.func  s7    66Q;;wwq#H7A.HOr   rP  r  r  	r&   r   r   is_param_free_rhsr   r!  r   r  r  s	          @@r   r   zNumPyCanonBackend.kron_l  s     "&!7!7$t!7!T   3x1}}!fxx1}}2#CII23q888HHQK%%	,,YG	 --dK\-]] 3r  c                    |t         j                  k7  sJ t        t        j                  |            }|t         j                  j
                  t        j                  t        j                  |      d      iiS )z
        Returns tensor of a variable node, i.e., eye(n) across axes 0 and 1, where n is
        the size of the variable.
        This function expands the dimension of an identity matrix of size n on the parameter axis.
        r   r  r   r   rB   r/   rG   r   expand_dimsrV  r&   r"   r*  rp  s       r   r   z%NumPyCanonBackend.get_variable_tensor  sV     hkk)))hkk//q	PQ1RSTTr   c                    | j                  |      }|j                  dd      }t        j                  j                  t        j                  j                  t        j                  |d      iiS )z
        Returns tensor of constant node as a column vector.
        This function expands the dimension of the column vector on the parameter axis.
        r   r   r   r   r   r  )r:  r   r   r   r   r/   r  r&   r   r   s      r   r   z!NumPyCanonBackend.get_data_tensor%  sT    
 ~~d#gS1!!HKK$5$5r~~fST7U#VWWr   c                    |t         j                  k7  sJ t        t        j                  |            }t         j                  j
                  |t        j                  t        j                  |      d      iiS )z
        Returns tensor of a parameter node, i.e., eye(n) across axes 0 and 2, where n is
        the size of the parameter.
        This function expands the dimension of an identity matrix of size n on the column axis.
        r   r  r  )r&   r"   r.  rp  s       r   r   z"NumPyCanonBackend.get_param_tensor.  sV     x{{***!!L"..QS2T#UVVr   c                t    	 | j                         }t        j                  |      }|S # t        $ r | }Y $w xY w)zX
        Internal function that converts a sparse input to a dense numpy array.
        )toarrayAttributeErrorr/   
atleast_2d)r   r   s     r   r:  zNumPyCanonBackend._to_dense9  s=    
	))+C mmC 
  	C	s   ) 77N)rv   r   rS   r   )r   dict[int, np.ndarray]r   r!   rS   r  rR   )rS   rB  )r   r   r   rB  rS   rB  )r   r   r   rB  rS   rB  r1  )r"   r2  r*  rB   rS    dict[int, dict[int, np.ndarray]])r   r   rS   r  )r"   r2  r.  rB   rS   r  )r   r   r   r3  r   r   r|   ry   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r:  r   r   r   rc   rc     s(     3,;3@U3 35#5?^6 
 
 	 	^( # #Jb(  <  (^T Q Q ^*^6^6	U/	UX	W/	W 	 	r   rc   c                  V   e Zd Zedd       Ze	 	 	 	 dd       Ze	 	 dd       ZddZed d       Zd!dZ		 	 d"dZ
ed!d       Zed!d	       Zd!d
Zd dZd#dZd$dZd!dZed!d       Zed%d       Zd!dZd&dZ	 	 d"dZed!d       Zd!dZd!dZd!dZ	 	 d'dZ	 	 d(dZ	 	 d)dZy)*rd   c                    t        j                  | j                        r| j                  ggn| j                  }t        j                  |      }|j
                  | j
                  k(  sJ |S r9  )r/   isscalarr   rH   	csr_arrayr"   )rv   r   r;  s      r   r   z.SciPyCanonBackend.get_constant_data_from_constG  sO    
 #%++fkk":<<%~~---r   c           	     ~    | j                         D ci c]  \  }}|t        j                  ||       c}}S c c}}w )a  
        Reshape constant data from column format to the required shape for operations that
        do not require column format. This function unpacks the constant data dict and reshapes
        the stacked slices of the tensor 'v' according to the lin_op_shape argument.
        )r=  rd   _reshape_single_constant_tensorr>  s       r   r   z'SciPyCanonBackend.reshape_constant_dataQ  sD     *//13Aq $DDQUU 3 	3 3   !9c                   | j                   d   dk(  sJ t        j                  | j                         t        j                  |      z  }| j                   d   |z  | j                   d   f}| j                         }|j                  |j
                  }}t        j                  ||d         \  }}t        j                  ||d         \  }	}
||d   z  |
z   }
||d   z  |d   f}t        j                  ||
|	ff|      S )z
        Given v, which is a matrix of shape (p * lin_op_shape[0] * lin_op_shape[1], 1),
        reshape v into a matrix of shape (p * lin_op_shape[0], lin_op_shape[1]).
        r   r   r*   )	r"   r/   rG   tocoor   r   divmodrH   rI   )r?  r   rq  	old_shapecoor   stacked_rowsslicesrK   new_colsr  new_stacked_shapes               r   r  z1SciPyCanonBackend._reshape_single_constant_tensor]  s     wwqzQGGAGG 55WWQZ1_aggaj1	ggi XXswwlyyy|<YYt\!_=(LO+h6a0,q/B||THh#78@QRRr   c                    t         j                  | j                  | j                  | j                  | j
                  | j                        S )z
        Returns an empty view of the corresponding SciPyTensorView subclass,
        coupling the SciPyCanonBackend subclass with the SciPyTensorView subclass.
        )SciPyTensorViewry   r\   r]   r^   r_   r`   r%   s    r   ry   z SciPyCanonBackend.get_empty_viewr  s?    
 --d.F.F6:6H6H$J[J[6:ooG 	Gr   c                .    d }|j                  |       |S )r   c                    |  S r$   r   )r   _ps     r   r   z#SciPyCanonBackend.neg.<locals>.func  r   r   r   r   s      r   r   zSciPyCanonBackend.neg{  s    
	 	tr   c                   | j                  |j                  |d      \  }}|rU|j                  |j                  d   z  }|dkD  r,t	        j
                  t	        j                  |d      |      n|fd}n]|j                  t        t        |j                                     j                  d   z  }|dkD  r| j                  ||      n|fd}|}|j                  ||	      S )
r   FrD  r   r   csrformatc                    |dk(  r| z  j                         S t        j                  t        j                  |d            | z  j	                         S Nr   cscr  tocsrrH   r   	eye_arraytocscr   rq  rI  s     r   r   z#SciPyCanonBackend.mul.<locals>.func  H    6'!O2244WWR\\!E%BKPTUU\\^^r   c                ^    j                         D ci c]  \  }}||| z   c}}S c c}}w r$   r=  rH  s      r   rJ  z/SciPyCanonBackend.mul.<locals>.parametrized_mul  s,    -8->->-@ATQ1q5AAAs   )rP  )r   r   rK   r"   rH   r   r  rS  rT  rU  _stacked_kron_rrX  )	r&   r   r   rY  rZ  r[  r   rJ  rI  s	           @r   r   zSciPyCanonBackend.mul  s     "&!7!7$u!7!U99		"-Dax!wwr||D'GM!_ 99T#**,%7 8 > >r BBDax"223=!B $D--dK\-]]r   c                R   t               }|j                         D ]  \  }}| j                  |   }|j                  d   |z  |j                  d   f}|j	                         }|j
                  |j                  |j                  }}
}	t        j                  |
|d         \  }}
t        j                  |
||d   z  |z  z   |      t        j                  t        j                  |      |d   z  t        |
            z   }t        j                  ||      t        j                  t        j                  |      |d   z  t        |            z   }t        j                  |	|      }|j                  d   |z  |j                  d   |z  f}t        j                  |||ff|      ||<    |S )a7  
        Given a stacked lhs
        [[A_0],
         [A_1],
         ...
        apply the Kronecker product with the identity matrix of size reps
        (kron(eye(reps), lhs)) to each slice, e.g., for reps = 2:
        [[A_0, 0],
         [0, A_0],
         [A_1, 0],
         [0, A_1],
         ...
        r   r   r*   )rR  r=  r^   r"   r  r   r   r   r/   r  repeatr_  r   r   rH   rI   )r&   rY  r[  r   param_idr?  rq  r  r  r   rK   rL   r  r  r  new_data	new_shapes                    r   r  z!SciPyCanonBackend._stacked_kron_r  sr    f99; 	CKHa""8,Aq!''!*5I'')C"xx#''$D99T9Q<8LFDyy1(=(D!DdK		$)A,6D	BCHyyt,		$)A,6D	BCHyyt,Hd*AGGAJ,=>ILLHh/0	CCM	C 
r   c                    t        t        j                  | j                              }t        j                  |      j                  t               }|j                  |       |S )z@
        Promote view by repeating along axis 0 (rows).
        )rB   r/   rG   r"   r  rE   r   )r   r   r`  rK   s       r   r   zSciPyCanonBackend.promote  sG    
 "''#)),-xx$++C0r   c                <   | j                   }| j                  d   j                   }t        j                  t        j                  |t
                    j                  |d      }t        j                  ||      j                  d      }|j                  |       |S rb  rc  rd  s        r   r   zSciPyCanonBackend.broadcast_to  rf  r   c                      j                  |j                  |d      \  }|rfd}n fd}|}|j                  ||      S )a;  
        Given (A, b) in view and constant data d, return (A*d, b*d).
        When dealing with parametrized constant data, we need to repeat the variable tensor p times
        and stack them vertically to ensure shape compatibility for elementwise multiplication
        with the parametrized expression.
        TrD  c                    |dk(  rj                  |       S t        j                  g|z        }|j                  |       S r^  multiplyrH   r   r   rq  new_lhsrY  s      r   r   z(SciPyCanonBackend.mul_elem.<locals>.func  s=    6<<?* ii	2G"++A..r   c                    j                         D ci c];  \  }}||j                  t        j                  | gj                  |   z              = c}}S c c}}w r$   )r=  r  rH   r   r^   )r   r  r?  rY  r&   s      r   rJ  z4SciPyCanonBackend.mul_elem.<locals>.parametrized_mul  sV    $'IIK1 Aq 1::biid6H6H6K0K&LMM 1 1 1s   A ArP  )r   r   rX  )r&   r   r   rZ  r   rJ  rY  s   `     @r   r   zSciPyCanonBackend.mul_elem  sQ     "&!7!7$t!7!T/1 $D--dK\-]]r   c                P    | j                   fd}|j                  |       |S )az  
        Given (A, b) in view, return the sum of the representation
        on the row axis, ie: (sum(A, axis=axis), sum(b, axis=axis)).
        Here, since the slices are stacked, we sum over the rows corresponding
        to the same slice.

        Note: we form the sparse output directly using _get_sum_row_indices and
        column indices in column-major order.
        c                l   t        j                  d   j                        }j                  \  }}|dk(  rS|Bt	        j
                  | j                  d      j                  d| j                  d               S  ||      }|| z  S | j                  d   |z  }|Qt	        j                  t	        j                  |d      t        j                  d|f            | z  j                         S  ||      }t	        j                  t	        j                  |d      |      | z  j                         S )Nr   r   r  )r"   r	  r  r  )rn  rg   r"   r   rH   r  rx   r   r   r  r/   r  r  )	r   rq  r"   r	  ro  Amr   sum_coeff_matrixs	          r   r   z+SciPyCanonBackend.sum_entries.<locals>.func  s    $))A,,,-EiiGD!Av<<<1(=(=a(LMM(u4@Aq5LGGAJ!O<GGBLL5$A277Aq6?SVWW^^``(u4@AGGBLL5$A1EIPPRRr   )_get_sum_coeff_matrixr   )r&   r   r   r   r  s    `  @r   r   zSciPyCanonBackend.sum_entries  s*      55	S" 	tr   c                
   t        j                  t        t        |            |d      }t        j                  |      |   }t        j
                  |      |   }t        j                  ||d      }|j                  d      S )a  
        Internal function that computes the row indices corresponding to the sum
        along a specified axis.

        Example:
        shape = (2,2,2) and axis = (1)
        out_axes = [True, False, True]
        out_idx[0] = [[[0, 0],
                       [0, 0]],
                      [[1, 1],
                       [1, 1]]]
        out_idx[1] = [[[0, 1],
                       [0, 1]],
                      [[0, 1],
                       [0, 1]]]
        out_dims = [2, 2]
        row_idx = [[[0, 2],
                    [0, 2]],
                   [[1, 3],
                    [1, 3]]]
        row_idx.flatten(order='F') = [0, 1, 0, 1, 2, 3, 2, 3]
        T)invertr   )dimsr   r   )r/   isinr   r   r   r0   ravel_multi_indexr   )r&   r"   r	  out_axesout_idxout_dimsr  s          r   _get_sum_row_indicesz&SciPyCanonBackend._get_sum_row_indices  si    . 775U,d4@**U#H-88E?8,&&wXSIS))r   c                |   t        |t              r|n|f}t        j                  |t              }t        j                  |D cg c]  }||   	 c}t              }| j                  ||      }t        j                  |      }t        j                  t        j                  |      ||ff||z  |f      }|S c c}w )zh
        Internal function that computes the sum coefficient matrix for a given shape and axis.
        r?   r*   )
r6   rn  r/   rG   rB   r  r   rH   r  r  )	r&   r"   r	  rp  r   rr  r  col_idxr  s	            r   r  z'SciPyCanonBackend._get_sum_coeff_matrix0  s     "$.tTGGGE%GGt,!U1X,C8++E48))A,LL"''!*w&89!Q$K	 -s   B9c                    | j                  |j                  |d      \  }|sJ t        j                  j                  t              _        fd}|j                  ||      S )ru  TrD  r?   c                    |dk(  rj                  |       S t        j                  g|z        }|j                  |       S r^  r  r  s      r   rx  z'SciPyCanonBackend.div.<locals>.div_funcJ  s=    Av||A&))SEAI.''**r   rP  )r   r   r/   rz  rA   rX  r{  s        @r   r   zSciPyCanonBackend.div<  sc     "&!7!7$t!7!T   ==7	+ --hO`-aar   c                    | j                   d   | j                   d   k(  sJ | j                  | j                   d   t        | j                   d   dz        fd}|j                  |       |S )r   r   r   r   c                   t        | j                        }t        |z        |d<   | j                         } t	        j
                  | j                  | j                  d   |z        \  }}dk(  r	|dz   z  }ndkD  r|dz   z  z  z   }n|dz   z  z
  }||z  z   j                  t              }t        j                  | j                  || j                  ff|      S rF  )r  r"   rB   r  r/   r  r   rE   rH   rI   r   r   )	r   rq  r"   x_slicex_rowr  r  rK   r   s	         r   r   z(SciPyCanonBackend.diag_vec.<locals>.func`  s    ME:>*E!H	AYYquuaggajAo>NGUAv D1H-Q D1H-q8 D1H-1 7Z#77??DH<<(AEE): ;UCCr   r  r  s      @@@r   r   zSciPyCanonBackend.diag_vecS  si     yy|syy|+++HHyy|1*+
	D 	tr   c                      fd}|S )r   c                f   | j                         }|j                  d   |z  }|j                  |z  }|j                  |dz   z  z   }|||z
  z
  j                  t              z  z   }t        j                  |j                  ||j                  fft	        |z        | j                  d   f      S )Nr   r   r*   )	r  r"   r   rE   rB   rH   rI   r   r   )r   rq  coo_reprr  r  r  r   r   s         r   r  z4SciPyCanonBackend.get_stack_func.<locals>.stack_funcw  s    ||~Hq!Q&A\\Q&F 
f'<<H&JNV,C+K+KC+P"PPH<<8<<0H I(+JN(;V\\!_'MO Or   r   r  s   `` r   r   z SciPyCanonBackend.get_stack_funcq  s    	O r   c           	         | j                  |j                  |d      \  }}t        |j                  d   j                        dk(  r|j                  d   j                  d   n|j                  d   j                  d   }|rt        |j                  j                        dk(  r||j                  d   k7  r|j
                  }|j                  |j                  d   z  }|dkD  r6t        j                  |j
                  t        j                  |d            n|j
                  fd}nEt        t        |j                                     \  }}	|	j                  d   | j                  |   z  }
t        |j                  j                        dk(  r|||
k7  rw|j                         D 	ci c]  \  }}	|| j                  |	|       }}}	t        t        |j                                     \  }}	|	j                  d   | j                  |   z  }
|j                  |
z  }|j                         D 	ci c]  \  }}	|| j                  |	|       }}}	|dkD  r| j                  ||      n|fd}|}|j!                  ||	      S c c}	}w c c}	}w )
r  FrD  r   r   r  r  c                    |dk(  r| z  j                         S t        j                  t        j                  |d            | z  j	                         S r  r  r  s     r   r   z$SciPyCanonBackend.rmul.<locals>.func  r  r   c                z    j                         D ci c]  \  }}||| z  j                          c}}S c c}}w r$   )r=  r  rH  s      r   rJ  z0SciPyCanonBackend.rmul.<locals>.parametrized_mul  s3    7B7H7H7JKtq!AE==?*KKKs   7rP  )r   r   r   rg   r"   TrK   rH   r   r  rS  rT  r=  r^   _transpose_stacked_stacked_kron_lrX  )r&   r   r   rY  rZ  r  r[  r   r  r?  r  rJ  rI  s               @r   r   zSciPyCanonBackend.rmul  s2    "&!7!7$u!7!U+.sxx{/@/@+AQ+F388A;$$Q'CHHUVKL]L]^_L`388>>"a'H		!,Dee99		!,Dax ggceeR\\$u-MN!ee_ SYY[)*DAqwwqzT%7%7%::H388>>"a'H,@ EHIIKPDAqq$11!Q77PPD-.1771:););A)>>99(D@C		L11d--a33LCLax"223=!L $D--dK\-]]# Q Ms   *J0J
c                   |j                   d   | j                  |   z  |j                   d   f}|j                   d   |d   z  }|d   |d   f}||d   z  |d   f}|j                         }|j                  |j                  |j
                  }	}}t        j                  ||d         \  }
}|	|
|d   z  z   }|}t        j                  |||ff|      S )a  
        Given v, which is a stacked matrix of shape (p * n, m), transpose each slice of v,
        returning a stacked matrix of shape (p * m, n).
        Example:
        Input:      Output:
        [[A_0],     [[A_0.T],
         [A_1],      [A_1.T],
          ...        ...
        r   r   r*   )
r"   r^   r  r   r   r   r/   r  rH   rI   )r&   r?  r  r  rq  r  r  r   rK   rL   r  r  r  s                r   r	  z$SciPyCanonBackend._transpose_stacked  s     WWQZ4#5#5h#??L	GGAJ)A,&q\9Q<0	1-y|<GGI66155!%%Ddyyy|4&9Q<//||THh#78@QRRr   c                   t               }|j                         D ]9  \  }}| j                  |    |j                         }|j                  |j
                  |j                  }	}}t        j                  ||z  |      t        j                  t        j                  |      t        |            z   }
t        j                  |	|z  |      t        j                  t        j                  |      t        |	            z   }t        j                  ||      }|j                  d   |z  |j                  d   |z  f}t        j                  ||
|ff|      ||<   < |S )a}  
        Given a stacked lhs with the following entries:
        [[a11, a12],
         [a21, a22],
         ...
        Apply the Kronecker product with the identity matrix of size reps
        (kron(lhs, eye(reps))) to each slice, e.g., for reps = 2:
        [[a11, 0, a12, 0],
         [0, a11, 0, a12],
         [a21, 0, a22, 0],
         [0, a21, 0, a22],
         ...
        r   r   r*   )rR  r=  r^   r  r   r   r   r/   r  r_  r   r   r"   rH   rI   )r&   rY  r[  r   r  r?  r  r   rK   rL   r  r  r  r  s                 r   r
  z!SciPyCanonBackend._stacked_kron_l  s    f99; 		CKHax('')C"xx#''$Dyyd3bggbiiosSWy6YYHyyd3bggbiiosSWy6YYHyyt,Hd*AGGAJ,=>ILLHh/0	CCM		C 
r   c                   | j                   d   j                  }t        j                  |d         |d   z  t        j                  |d         z   }t        j                  t        |            }t        j                  t        |            |j                  t              f}t        j                  ||fdt        j                  |      f      dfd}|j                  |d      S )z
        Select the rows corresponding to the diagonal entries in the expression and sum along
        axis 0.
        Apply kron(eye(p), lhs) to deal with parametrized expressions.
        r   r   r*   c                    |dk(  r| z  j                         S t        j                  t        j                  |d            | z  j	                         S r  r  r   rq  rY  s     r   r   z%SciPyCanonBackend.trace.<locals>.func  sE    Ava((Qu =sCaGNNPPr   TrP  )rS   rU   )rg   r"   r/   r   r  r   r  rE   rB   rH   r  rG   rX  )r   r   r"   r   r   idxr   rY  s          @r   r   zSciPyCanonBackend.trace  s     !!))E!H%a0299U1X3FFwws7|$xxG%w~~c':;llD#;q"''%..AB	Q --d4-PPr   c                   | j                  |j                  |d      \  }|sJ d       j                  dk(  sJ t        |j                  j                        dk(  rj
                  |j                  d   }t        |j                  d   j                        dkD  r|j                  d   j                  d   nd}j                         j                  }t        j                  j                  |      t        j                  t        j                  |      |      z   j                  t              }t        j                  j                   |      t        j                  t        j                  |      |      z   j                  t              }t        j                  j                  |      }	t#        j$                  |	||ff||f      fd}
|j'                  |
|	      S )
r  FrD  zBSciPy backend does not support parametrized left operand for conv.r   r   r   r*   c                &    |dk(  sJ d       | z  S )Nr   zCSciPy backend does not support parametrized right operand for conv.r   r  s     r   r   z$SciPyCanonBackend.conv.<locals>.func!  s$    6 VUV67Nr   rP  )r   r   r  r   r"   r  rg   r  nnzr/   r_  r   r  r   rE   rB   r   rH   r  rX  )r&   r   r   rZ  rK   rL   nonzerosr  r  r   r   rY  s              @r   r   zSciPyCanonBackend.conv  s~    "&!7!7$u!7!U  	QP	Q xx1}}sxx~~!#%%Cyy|'*388A;+<+<'='Asxx{  #qiik7777377D)BIIbiiox,PPXXY\]77377D)BIIbiiox,PPXXY\]wwsxx&llD7G"45dD\J	
 --dK\-]]r   c                   | j                  |j                  |d      \  }|sJ d       j                  dk(  sJ t        |j                  D ch c]  }|j
                   c}      dk(  sJ |j                  d   j
                  }| j                  |j                  j
                  |      fd}|j                  ||      S c c}w )	r  TrD  zDSciPy backend does not support parametrized left operand for kron_r.r   r   r   c                    |dk(  sJ d       | j                   dk(  sJ t        j                  |       j                         }|d d f   }|S )Nr   zESciPy backend does not support parametrized right operand for kron_r.r   r  rH   r   r  )r   rq  r  rY  r  s      r   r   z&SciPyCanonBackend.kron_r.<locals>.func:  sV    6 XWX666Q;;wwsA,,.H
+HOr   rP  r   r   r  r   rg   r"   r(  rX  r  s	          @@r   r   zSciPyCanonBackend.kron_r(  s     "&!7!7$t!7!T  	SR	S xx1}}2#CII23q888HHQK%%	,,SXX^^YG	 --dK\-]] 3   Cc                   | j                  |j                  |d      \  }|sJ d       j                  dk(  sJ t        |j                  D ch c]  }|j
                   c}      dk(  sJ |j                  d   j
                  }| j                  ||j                  j
                        fd}|j                  ||      S c c}w )	r  TrD  zESciPy backend does not support parametrized right operand for kron_l.r   r   r   c                    |dk(  sJ d       | j                   dk(  sJ t        j                  |       j                         }|d d f   }|S )Nr   zDSciPy backend does not support parametrized left operand for kron_l.r   r  )r   rq  r  r  r  s      r   r   z&SciPyCanonBackend.kron_l.<locals>.funcV  sV    6 WVW666Q;;wwq#,,.H
+HOr   rP  r  r  s	          @@r   r   zSciPyCanonBackend.kron_lD  s     "&!7!7$t!7!T  	TS	T xx1}}2#CII23q888HHQK%%	,,YG	 --dK\-]] 3r  c                    |t         j                  k7  sJ t        t        j                  |            }|t         j                  j
                  t        j                  |d      iiS )z
        Returns tensor of a variable node, i.e., eye(n) across axes 0 and 1, where n is
        the size of the variable.
        This function returns eye(n) in csc format.
        r  r  )r   r   rB   r/   rG   r   rH   r  r  s       r   r   z%SciPyCanonBackend.get_variable_tensor`  sM     hkk)))hkk//a1NOPPr   c                X   t        |t        j                        r't        j                  |j                  dd            }n4t        j                  |      j                  dd      j                         }t        j                  j                  t        j                  j                  |iiS )z
        Returns tensor of constant node as a column vector.
        This function reshapes the data and converts it to csc format.
        r  r   r   )r6   r/   rW  rH   r  r   
coo_matrixr  r   r   r   r  s      r   r   z!SciPyCanonBackend.get_data_tensork  sx     dBJJ'\\$,,wc,"BCF]]4(000DJJLF!!HKK$5$5v#>??r   c                   |t         j                  k7  sJ | j                  |   }t        t	        j
                  |      |z        df}t	        j                  |      t	        j                  |      t	        j                  |      |z  z   t	        j                  |      ff}t        j                  ||      }t         j                  j                  ||iiS )z
        Returns tensor of a parameter node, i.e., eye(n) across axes 0 and 2, where n is
        the size of the parameter.
        This function returns eye(n).flatten() in csc format.
        r   )r   r   r^   rB   r/   rG   r  r   r  rH   rI   r   )r&   r"   r.  
param_sizer   	param_vecs         r   r   z"SciPyCanonBackend.get_param_tensorx  s     x{{***''5
RWWU^j0115ggj!BIIj$9BIIj<QT^<^$^$&HHZ$8$: :LLe,	!!L)#<==r   N)rv   r   rS   sp.csr_array)r   dict[int, sp.csc_array]r   r!   rS   r#  )r?  rU   r   r!   rS   rU   )rS   r  )r   r   r   r  rS   r  )r   r   r   r  rS   r  )rY  zdict[int, list[sp.csc_array]]r[  rB   rS   rU   )r"   rn  r	  rn  rS   r   )r"   rn  r	  rn  rS   r"  r1  )r?  rU   r  rB   rS   rU   )r"   r2  r*  rB   rS   "dict[int, dict[int, sp.csc_array]])r   r0  rS   z"dict[int, dict[int, sp.csr_array]])r"   r2  r.  rB   rS   r$  )r   r   r   r3  r   r   r  ry   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   rd   rd   F  s_     	3,;	3&	3 	3 SS S(G  ^B@   	 	^.>*:
b.  :   5^nS08 Q Q*#^J^8^8	Q.	Q@.@>.>r   rd   c                      e Zd ZdZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZeedd              Ze		 	 	 	 	 	 	 	 dd       Z
edd       Zeedd              Zedd       Zedd	       Zedd
       Ze	 	 dd       Zy)r/  z
    A TensorView represents the tensors for A and b, which are of shape
    rows x var_length x param_size_plus_one and rows x 1 x param_size_plus_one, respectively.
    The class facilitates the application of the CanonBackend functions.
    c	                |    ||nd | _         || _        || _        || _        || _        || _        || _        || _        y r$   )r   r   r   r\   r]   r^   r_   r`   )	r&   r   r   r   r\   r]   r^   r_   r`   s	            r   ra   zTensorView.__init__  sJ     -9,DL$!2 $7 "*($r   c                   t        || j                        sJ | j                  |j                  z  | _        | j                  | j                  |j                        | _        | j
                  xr |j
                  | _        | S r$   )r6   	__class__r   combine_potentially_noner   r   r7   s     r   __iadd__zTensorView.__iadd__  si    %000 --0B0BB33DKKN!%!7!7!SE<S<Sr   c                     yz
        Adds the tensor a to b if they are both not none.
        If a (b) is not None but b (a) is None, returns a (b).
        Returns None if both a and b are None.
        Nr   rs  bs     r   r)  z#TensorView.combine_potentially_none  r   r   c           
           | ddd|||||      S )zN
        Return a TensorView that has shape information, but no data.
        NTr   )r3   r\   r]   r^   r_   r`   s         r   ry   zTensorView.get_empty_view  s$     4t%8)]T` 	r   c                >    | t         j                  j                  hk(  S )zA
        Does the TensorView only contain constant data?
        )r   r   r   )r   s    r   is_constant_datazTensorView.is_constant_data  s    
  1 1222r   c                     y)z3
        Number of rows of the TensorView.
        Nr   r%   s    r   rK   zTensorView.rows  r   r   c                     y)z 
        Returns [A b].
        Nr   )r&   r   r   s      r   r{   z$TensorView.get_tensor_representation  r,  r   c                     y)z,
        Select 'rows' from tensor.
        Nr   )r&   rK   s     r   r   zTensorView.select_rows  r,  r   c                     y)zI
        Apply 'func' across all variables and parameter slices.
        Nr   )r&   r   s     r   r   zTensorView.apply_all  r,  r   c                     y)zZ
        Create new TensorView with same shape information as self, but new data.
        Nr   r&   r   r   r   s       r   r   z!TensorView.create_new_tensor_view  r   r   N)r   zset[int] | Noner   r   r   rT   r\   rB   r]   rn   r^   rn   r_   rn   r`   rB   )r8   r/  rS   r/  )rs  
Any | Noner.  r8  rS   r8  )r\   rB   r]   rn   r^   rn   r_   rn   r`   rB   rS   r/  )r   set[int]rS   rT   rS   rB   r   rB   r   rB   rS   r   rK   r   rS   Noner   r	   rS   r=  )r   r9  r   r   r   rT   rS   r/  )r   r   r   rV   ra   r*  r3  r   r)  rX   ry   r1  propertyrK   r{   r   r   r   r   r   r   r/  r/    s7   %.%% %)% '*	%
 +% !/%  .% !%*    &4DR#&   3 3           r   r/  c                  ^    e Zd ZdZddZd	dZeed
d              Zeed               Z	ddZ
y)DictTensorViewa  
    The DictTensorView abstract class handles the dictionary aspect of the tensor representation,
    which is shared across multiple backends.
    The tensor is contained in the following data structure:
    `Dict[variable_id, Dict[parameter_id, tensor]]`, with the outer dict handling
    the variable offset, and the inner dict handling the parameter offset.
    Subclasses have to implement the implementation of the tensor, as well
    as the tensor operations.
    c                    | j                   j                         D ]I  \  }}|r| j                  ||      n" ||t        j                  j
                           | j                   |<   K | j                  xr || _        | S )ar  
        Apply 'func' to A and b.
        If 'func' is a parameter free function, then we can apply it to all parameter slices
        (including the slice that contains non-parameter constants).
        If 'func' is not a parameter free function, we only need to consider the parameter slice
        that contains the non-parameter constants, due to DPP rules.
        )r   r=  apply_to_parametersr   r   r   r   )r&   r   rQ  r*  r   s        r   rX  z(DictTensorView.accumulate_over_variables  s|     $(;;#4#4#6 	LK& (,'?'?f'M,08I8I1J,K KK$	L "&!7!7!R<Rr   c                H    ||y|||S |||S | j                  ||      S r,  )	add_dicts)r&   rs  r.  s      r   r)  z'DictTensorView.combine_potentially_none  s=     9]qyHY1=H>>!Q''r   c                     y)zP
        Returns element-wise addition of two tensors of the same type.
        Nr   r-  s     r   add_tensorszDictTensorView.add_tensors  r   r   c                      y)z;
        Returns the type of the underlying tensor
        Nr   r   r   r   tensor_typezDictTensorView.tensor_type  r   r   c                6   i }t        |j                               }t        |j                               }||z  }|D ]  }t        ||   t              r/t        ||   t              r| j	                  ||   ||         ||<   Et        ||   | j                               r9t        ||   | j                               r| j                  ||   ||         ||<   t        d| j                          d       ||z
  D ]
  }||   ||<    ||z
  D ]
  }||   ||<    |S )z2
        Addition for dict-based tensors.
        zValues must either be dicts or .)setkeysr6   rR  rE  rI  rG  r;   )r&   rs  r.  r   keys_akeys_b	intersectkeys           r   rE  zDictTensorView.add_dicts&  s,    QVVXQVVXVO	 	ZC!C&$'Jqvt,D>>!C&!C&9CAcFD$4$4$67JqvtO_O_Oa<b++AcFAcF;C #B4CSCSCUBVVW!XYY	Z I% 	CvCH	I% 	CvCH	
r   N)r   r	   rQ  rT   rS   r/  )rs  dict | Noner.  rR  rS   rR  )rs  r   r.  r   rS   r   )rs  rR  r.  rR  rS   rR  )r   r   r   rV   rX  r)  r3  r   rG  rI  rE  r   r   r   rA  rA    sN    (      r   rA  c                      e Zd Zed
d       ZddZddZddZ	 	 	 	 ddZe		 	 	 	 dd       Z
e	dd       Ze	d        Zy	)rB  c           
         | j                   Yt        t        t        t        | j                   j                                     j                                     j                  d   S t
        )zj
        Number of rows of the TensorView.
        This is the second dimension of the 3d tensor.
        r   )r   rS  rT  rU  r"   r;   r%   s    r   rK   zNumPyTensorView.rows>  sO     ;;"T$t{{'9'9';"<=DDFGHNNqQQr   c                   | j                   J || j                  dz   f}g }| j                   j                         D ]"  \  }}|j                         D ]  \  }}|j                  \  }	}
}|j	                  t        |j                  d      t        j                  t        j                  t        j                  |
      |      |	      |z   t        j                  t        j                  t        j                  |      |
      |	      | j                  |   z   t        j                  t        j                  |	      |
|z        | j                  |   z   |              % t
        j                  |      S )a  
        Returns a TensorRepresentation of [A b] tensor.
        This function iterates through all the tensor data and constructs the
        respective representation in COO format. To obtain the data, the tensor must be
        flattened as it is not in a sparse format. The row and column indices are obtained
        with numpy tiling/repeating along with their respective offsets.

        Note: CVXPY currently only supports usage of sparse matrices after the canonicalization.
        Therefore, we must return tensor representations in a (data, (row,col)) format.
        This could be changed once dense matrices are accepted.
        r   r   r   r*   )r   r`   r=  r"   r1   r   r   r/   r  r_  r   r]   r_   r4   )r&   r   r   r"   tensor_representationsr*  r   r.  parameter_tensorr   rK   rL   s               r   r{   z)NumPyTensorView.get_tensor_representationI  s@    {{&&&T__q01!#,0KK,=,=,? 
	(K2A2G2G2I 	..)9)?)?&
D$&--.B$,,3,7IIbggbiiot<jIJVIIbii		$>
Knn[12GGBIIj14$;?$BSBST`Baa/ 	
	 $++,BCCr   c                2    fd}| j                  |       y)z
        Select 'rows' from tensor.
        The rows of the 3d tensor are in axis=1, this function selects a subset
        of the original tensor.
        c                    | d d d d f   S r$   r   )r   rK   s    r   r   z)NumPyTensorView.select_rows.<locals>.funck  s    QaZ= r   Nr   r&   rK   r   s    ` r   r   zNumPyTensorView.select_rowse  s    	! 	tr   c                    | j                   j                         D ci c]0  \  }}||j                         D ci c]  \  }}| ||       c}}2 c}}}}| _         yc c}}w c c}}}}w )z
        Apply 'func' across all variables and parameter slices.
        The tensor functions in the NumPyBackend manipulate 3d arrays.
        Therefore, this function applies 'func' directly to the tensor 'v'.
        N)r   r=  r&   r   var_idparameter_reprr  r?  s         r   r   zNumPyTensorView.apply_allp  sv     6:[[5F5F5HJ J16> ,:,@,@,B D$(Aq !"47
  D D J  D Js   A'
A!A'
!A'
c           
         t        |||| j                  | j                  | j                  | j                  | j
                        S )zn
        Create new NumPyTensorView with same shape information as self,
        but new tensor data.
        )rB  r\   r]   r^   r_   r`   r7  s       r   r   z&NumPyTensorView.create_new_tensor_viewz  s>     |V5FH`H`#~~t/A/A4CTCT#0 	0r   c                b    |j                         D ci c]  \  }}| | |       c}}S c c}}w )zT
        Apply 'func' to the entire tensor of the parameter representation.
        r  )r   parameter_representationr  r?  s       r   rC  z#NumPyTensorView.apply_to_parameters  s.     (@'E'E'GHtq!47
HHHs   +c                    | |z   S )zG
        Apply element-wise addition on two dense numpy arrays
        r   r-  s     r   rG  zNumPyTensorView.add_tensors      
 1ur   c                 "    t         j                  S )zP
        The tensor is represented as a 3-dimensional dense numpy array
        )r/   rW  r   r   r   rI  zNumPyTensorView.tensor_type  s    
 zzr   Nr:  r;  r<  r>  )r   r9  r   r   r   rT   rS   rB  )r   r	   ra  r  rS   r  )rs  r   r.  r   rS   r   )r   r   r   r?  rK   r{   r   r   r   r3  rC  rG  rI  r   r   r   rB  rB  <  s     D8	J0260;J0 I6KI$I I    r   rB  c                  x    e Zd Zed
d       ZddZddZddZ	 	 	 	 ddZ	 	 	 	 ddZ	e
dd       Ze
d        Zy	)r  c                    | j                   Z| j                   j                         D ]<  }|j                         D ]'  \  }}|j                  d   | j                  |   z  c c S  > yt        d      )z
        Number of rows of the TensorView.
        This is calculated by dividing the totals rows of the tensor by the
        number of parameter slices.
        Nr   zTensor cannot be None)r   rU  r=  r"   r^   r;   )r&   
param_dictr  	param_mats       r   rK   zSciPyTensorView.rows  sx     ;;""kk002 N
+5+;+;+= N'Hi$??1-1C1CH1MMMNN 455r   c                h   | j                   J || j                  dz   f}g }| j                   j                         D ]  \  }}|j                         D ]  \  }}| j                  |   }	|j                  d   |	z  }
|j                  d      }|j                  t        |j                  |j                  |
z  |z   |j                  | j                  |   z   |j                  |
z  t        j                  |j                        | j                  |   z  z   |               t        j!                  |      S )a  
        Returns a TensorRepresentation of [A b] tensor.
        This function iterates through all the tensor data and constructs their
        respective representation in COO format. The row data is adjusted according
        to the position of each element within a parameter slice. The parameter_offset
        finds which slice the original row indices belong to before applying the column
        offset.
        r   r   F)copyr*   )r   r`   r=  r^   r"   r  r1   r   r   r   r   r]   r/   r  r  r_   r4   )r&   r   r   r"   rV  r*  r   r.  parameter_matrixrq  r  r  s               r   r{   z)SciPyTensorView.get_tensor_representation  s-    {{&&&T__q01!#,0KK,=,=,? 	(K2A2G2G2I 
..&&|4$**1-2+11u1=&--.BMM\\A%3LL4>>+#>>LLA%(=@Q@QR^@_(__/ 	
	 $++,BCCr   c                2    fd}| j                  |       y)z
        Select 'rows' from tensor. If there are multiple parameters 'p',
        we must select the same 'rows' from each parameter slice. This is done by
        introducing an offset of size 'm' for every parameter.
        c                    |dk(  r	| d d f   S | j                   d   |z  }| t        j                  |      t        j                  t        j                  |      |z  t                    z   d d f   S r~  )r"   r/   r_  r  r   r   )r   rq  r  rK   s      r   r   z)SciPyTensorView.select_rows.<locals>.func  sf    Avqz!GGAJ!Oq)BIIbiilQ6FD	,RRTUUVVr   Nr   rZ  s    ` r   r   zSciPyTensorView.select_rows  s    	W 	tr   c                    | j                   j                         D ci c]>  \  }}||j                         D ci c]  \  }}| ||| j                  |          c}}@ c}}}}| _         yc c}}w c c}}}}w )z
        Apply 'func' across all variables and parameter slices.
        For the stacked-slices backend, we must pass an additional parameter 'p'
        which is the number of parameter slices.
        N)r   r=  r^   r\  s         r   r   zSciPyTensorView.apply_all  s     6:[[5F5F5HJ J16> ,:,@,@,B D$(Aq !"44+=+=a+@#A A  D D J  D Js   A5
!A/A5
/A5
c           
         t        |||| j                  | j                  | j                  | j                  | j
                        S )zn
        Create new SciPyTensorView with same shape information as self,
        but new tensor data.
        )r  r\   r]   r^   r_   r`   r7  s       r   r   z&SciPyTensorView.create_new_tensor_view  s?     |V5F'+'?'?'+'9'94;L;L'+8 	8r   c           
     ~    |j                         D ci c]  \  }}| ||| j                  |          c}}S c c}}w )z
        Apply 'func' to each slice of the parameter representation.
        For the stacked-slices backend, we must pass an additional parameter 'p'
        which is the number of parameter slices.
        )r=  r^   )r&   r   ra  r  r?  s        r   rC  z#SciPyTensorView.apply_to_parameters  s=     ?W>\>\>^_da44--a011___r  c                    | |z   S )zF
        Apply element-wise summation on two sparse matrices.
        r   r-  s     r   rG  zSciPyTensorView.add_tensors  rc  r   c                 B    t         j                  t         j                  fS )z
        The tensor representation of the stacked slices backend is one big
        sparse matrix instead of smaller sparse matrices in a list.
        )rH   sparrayspmatrixr   r   r   rI  zSciPyTensorView.tensor_type  s     

BKK((r   Nr:  r;  r<  r>  )r   r9  r   r   r   rT   rS   r  )r   r	   ra  dict[int, sp.spmatrix]rS   ru  )rs  sp.spmatrixr.  rv  rS   rv  )r   r   r   r?  rK   r{   r   r   r   rC  r3  rG  rI  r   r   r   r  r    sz    6 6D4J	826	8;J	8`6L`%`   ) )r   r  )'rV   
__future__r   abcr   r   dataclassesr   enumr   typingr   r	   numpyr/   scipy.sparsesparserH   scipy.signalr   cvxpy.settingssettingsr   cvxpy.lin_opsr   r   r   r   r   r   rZ   rr   re   rc   rd   r/  rA  rB  r  r   r   r   <module>r     s    # # !      !   t  J] J] J]Z;3 ;|n nb7| 7(E* EP>* >Db bJLZ L^]n ]@e)n e)r   