
    bibj                    L   d Z ddlmZ ddlmZmZ ddlmZmZm	Z	m
Z
mZmZmZ erddl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 ddlmZ ddlmZ ddl m!Z! ddl"m#Z#  ed       G d d             Z$ ed       G d d             Z% ed       G d d             Z& ed       G d d             Z'ee$e%e&e'f   Z(e G d d             Z)e G d d             Z*d'dZ+d(dZ,d)d Z-	 	 	 	 	 	 	 	 	 	 d*d!Z.	 	 	 	 	 	 	 	 	 	 d+d"Z/d,d#Z0	 	 	 	 	 	 d-d$Z1 G d% d&e!      Z2y).a  
Copyright 2025, 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.

SOC dimension reduction to 3D cones.

This module provides the SOCDim3 reduction which converts arbitrary-dimensional
Second-Order Cone (SOC) constraints to equivalent systems of 3D SOC constraints.
This enables solvers that only support 3D SOC to handle arbitrary dimensional
SOC constraints.

The decomposition uses a binary tree structure:
- Dimension 1: Convert to NonNeg constraint (||[]|| <= t becomes t >= 0)
- Dimension 2: Convert to NonNeg constraints (|x| <= t becomes t-x >= 0, t+x >= 0)
- Dimension 3: Pass through unchanged
- Dimension 4: Chain of two 3D cones (special case for efficiency)
- Dimension n > 4: Binary split into balanced tree of 3D cones

Example:
    A dimension 5 cone ||(x1, x2, x3, x4)|| <= t becomes::

        ||(x1, x2)|| <= s1  (3D cone)
        ||(x3, x4)|| <= s2  (3D cone)
        ||(s1, s2)|| <= t   (3D cone)

    where s1, s2 are auxiliary variables.
    )annotations)	dataclassfield)TYPE_CHECKINGAnyDictListOptionalTupleUnion)ProblemNreshape)vstack)NonNeg)SOC)cvxtypes)
Expression)Variable)	Reduction)SolutionT)frozenc                  .    e Zd ZU dZdZded<   dZded<   y)	
NonNegTreea  Tree node for cases that reduce to NonNeg constraints.

    - Dim-1 (||[]|| <= t): reduces to t >= 0
    - Dim-2 (|x| <= t): reduces to t-x >= 0, t+x >= 0

    Attributes
    ----------
    original_dim : int
        Original dimension (1 or 2).
    nonneg_ids : tuple
        Constraint IDs of the NonNeg constraints.
        For dim-1: (id of t >= 0,)
        For dim-2: (id of t-x >= 0, id of t+x >= 0)
       intoriginal_dim tuple
nonneg_idsN)__name__
__module____qualname____doc__r   __annotations__r    r       ^/home/cdr/jupyterlab/.venv/lib/python3.12/site-packages/cvxpy/reductions/cone2cone/soc_dim3.pyr   r   @   s     L#Jr&   r   c                  *    e Zd ZU dZded<   dZded<   y)LeafTreea/  Tree node for a single 3D cone.

    Represents a leaf in the decomposition tree - an actual 3D SOC constraint.

    Attributes
    ----------
    cone_id : int
        The constraint ID of the 3D SOC cone.
    original_dim : int
        The original dimension of this cone (always 3 for LeafTree).
    r   cone_id   r   Nr!   r"   r#   r$   r%   r   r   r&   r'   r)   r)   T   s    
 LL#r&   r)   c                  4    e Zd ZU dZded<   ded<   dZded<   y)	ChainTreea  Tree node for dim-4 special case: two chained cones.

    For dimension 4 (||(x1, x2, x3)|| <= t), we use a chain structure:
        ||(x1, x2)|| <= s
        ||(s, x3)|| <= t

    This avoids the unbalanced 1+2 split that would occur with generic binary split.

    Attributes
    ----------
    leaf_cone_id : int
        Constraint ID of the leaf cone (contains x1, x2).
    root_cone_id : int
        Constraint ID of the root cone (contains s, x3).
    original_dim : int
        Always 4 for chain decomposition.
    r   leaf_cone_idroot_cone_id   r   Nr,   r   r&   r'   r.   r.   e   s    " L#r&   r.   c                  :    e Zd ZU dZded<   ded<   ded<   ded<   y)		SplitTreea  Tree node for binary split decomposition.

    For dimension n > 4, we split x into two halves and recursively decompose:
        ||x_left|| <= s1
        ||x_right|| <= s2
        ||(s1, s2)|| <= t

    Attributes
    ----------
    root_cone_id : int
        Constraint ID of the root cone (contains s1, s2).
    left : DecompositionTree
        Left subtree (decomposition of x_left).
    right : DecompositionTree
        Right subtree (decomposition of x_right).
    original_dim : int
        Original dimension of this cone.
    r   r0   DecompositionTreeleftrightr   N)r!   r"   r#   r$   r%   r   r&   r'   r3   r3   }   s!    $ 
r&   r3   c                  >    e Zd ZU dZded<   ded<   ded<   dZded<   y	)
SOCTreeDataa  Decomposition data for a single original SOC constraint.

    Attributes
    ----------
    trees : List[DecompositionTree]
        List of decomposition trees, one per cone in elementwise SOC.
    num_cones : int
        Number of individual cones (1 for simple SOC, >1 for elementwise).
    original_dim : int
        Dimension of each cone.
    axis : int
        Axis parameter from original SOC constraint.
    zList[DecompositionTree]treesr   	num_conesr   r   axisN)r!   r"   r#   r$   r%   r;   r   r&   r'   r8   r8      s#     #"ND#Mr&   r8   c                  J    e Zd ZU dZ ee      Zded<    ee      Z	ded<   y)SOCDim3InverseDataa*  Data needed to reconstruct original SOC duals from decomposed problem.

    Attributes
    ----------
    soc_trees : Dict[int, SOCTreeData]
        Mapping from original SOC constraint ID to its decomposition data.
    old_constraints : List
        Reference to original problem constraints.
    )default_factoryzDict[int, SOCTreeData]	soc_treesr	   old_constraintsN)
r!   r"   r#   r$   r   dictr?   r%   listr@   r   r&   r'   r=   r=      s(     ).d(CI%C!$7OT7r&   r=   c                    t        | dd      S )a  Reshape expression to scalar form (1,) for SOC constructor.

    Parameters
    ----------
    expr : Expression
        Expression to reshape (should be scalar or shape (1,)).

    Returns
    -------
    Expression
        Expression with shape (1,).
    )r   Forderr   )exprs    r'   _to_scalar_shaperH      s     4S))r&   c                *   | yt        | t              rlt        |       dk(  r^| \  }}||yt        j                  |      }t        j                  |      j                  d      }t        j                  |dd |g      S t        j                  |       S )a  Convert a dual value to flat array format.

    Dual values can come in two formats:
    - Flat array: [lambda, mu1, mu2, ...] (from solver)
    - Split list: [lambda_array, mu_array] (after save_dual_value)

    This function normalizes to flat array format.

    Parameters
    ----------
    dual_value : Optional[Any]
        Dual value in either format, or None.

    Returns
    -------
    Optional[np.ndarray]
        Flat array [lambda, mu1, mu2, ...] or None if input is None.
    N   rD   rE   r   )
isinstancerB   lennp
atleast_1dflattenconcatenate)
dual_valuet_valx_valt_arrx_arrs        r'   _get_flat_dualrV      s    &  *d#J1(<!u=EMe$e$,,3,7~~uRay%011 ==$$r&   c                   t        | t              rt        | j                        S t        | t              r| j
                  hS t        | t              r| j                  | j                  hS t        | t              rW| j                  h}|j                  t        | j                               |j                  t        | j                               |S t               S )zGet all constraint IDs from a decomposition tree.

    Parameters
    ----------
    tree : DecompositionTree
        The decomposition tree to traverse.

    Returns
    -------
    set
        Set of all constraint IDs (SOC and NonNeg) in the tree.
    )rK   r   setr    r)   r*   r.   r/   r0   r3   update_get_cone_ids_from_treer5   r6   )treeidss     r'   rZ   rZ      s     $
#4??##	D(	#~	D)	$!!4#4#455	D)	$  !

*49956

*4::67
5Lr&   c                   | |t        d      |j                  }|dk(  r4t        |       }|j                  |       t	        d|j
                  f      S |dk(  rd|j                  d      }t        | |z
        }t        | |z         }|j                  ||g       t	        d|j
                  |j
                  f      S |dk(  rLt        t        |       |j                  d            }	|j                  |	       t        |	j
                  	      S |d
k(  rt               }
|j                  t        |
             |dd }|d   }t        t        |
      |j                  d            }|j                  |       t        |
|g      }t        t        |       |j                  d            }|j                  |       t        |j
                  |j
                  d      S |dz   dz  }|d| }||d }t               }t               }|j                  t        |             |j                  t        |             t        ||j                  d      ||      }t        ||j                  d      ||      }t        ||g      }t        t        |       |j                  d            }|j                  |       t        |j
                  |||dz         S )a  Decompose a single ||x|| <= t constraint into tree of exactly 3D cones.

    This function recursively decomposes an n-dimensional SOC constraint into
    a tree of 3D SOC constraints using binary splitting.

    Parameters
    ----------
    t_expr : Expression
        The scalar bound expression (should have size 1).
    x_expr : Expression
        The vector argument (1D expression).
    soc3_out : List[SOC]
        Output list to append generated 3D SOC constraints to.
    nonneg_out : List[NonNeg]
        Output list to append generated NonNeg constraints to.

    Returns
    -------
    DecompositionTree
        Tree structure for dual variable reconstruction.

    Raises
    ------
    ValueError
        If t_expr or x_expr is None, or if t_expr is not scalar.
    Nz t_expr and x_expr cannot be Noner   r   )r   r    rD   rE   rJ   )r*   r+   r1   )r/   r0   r   )r0   r5   r6   r   )
ValueErrorsizer   appendr   idrO   extendr   rH   r)   r   r   r.   _decompose_soc_singler3   )t_exprx_exprsoc3_out
nonneg_outncx_flatc1c2conesx_leftx_lastcone1root_xcone2midx_rights1s2	left_tree
right_tree	root_cones                          r'   rc   rc     s   B ~;<<A 	Av6N!qaddW== 	Avc* FVO$FVO$2r(#qbeeRUU^DD 	Av#F+V^^#^-FG(( 	Av J&)$ $Q'c)BC F$$V,fnn3n.GH
 	
 q5Q,CDS\FSTlG 
B	BfRj!fRj! &b&..s.*CXzZI&r7???+ExQ[\J RHF$V,fnn3n.GHIOOI\\U	 r&   c                   t        | t              r| j                  dk(  r|S | j                  dk(  rt        | j                        dk(  r|j                  | j                  d         }|j                  | j                  d         }||yt        t        j                  |      j                  d         }t        t        j                  |      j                  d         }||z
  ||<   |dz   S |S t        | t              rW|j                  | j                        }|yt        |      }	|	t        |	      dk  ry|	dd }
t        |
      }|
||||z    ||z   S t        | t              r|j                  | j                        }|j                  | j                        }||yt        |      }t        |      }||yt        |      dk\  r|d   ||<   |d   ||dz   <   |dz  }t        |      dk\  r|d   ||<   |dz  }|S t        | t               r3t#        | j$                  |||      }|yt#        | j&                  |||      S y)a#  Collect x-component duals from leaf cones into pre-allocated array.

    For an SOC constraint ||x|| <= t, the dual is a flat array [lambda, mu1, mu2, ...]
    where lambda is the dual for t and mu is the dual for x.

    This function traverses the decomposition tree and writes all mu components
    from leaf cones into the output array in order.

    Parameters
    ----------
    tree : DecompositionTree
        The decomposition tree structure.
    dual_vars : Dict[int, Any]
        Mapping from constraint ID to dual variable value.
    out : np.ndarray
        Pre-allocated output array to write duals into.
    offset : int
        Current write position in the output array.

    Returns
    -------
    Optional[int]
        New offset after writing, or None if reconstruction failed.
    r   rJ   r   Nr+   )rK   r   r   rL   r    getfloatrM   rN   flatr)   r*   rV   r.   r/   r0   r3   _collect_x_duals_into_arrayr5   r6   )r[   	dual_varsoutoffset	alpha_rawbeta_rawalphabetadual_rawdualx_dualn_writeleaf_dual_rawroot_dual_raw	leaf_dual	root_dual
new_offsets                    r'   r   r     sI   < $
#!M!#DOO(<(A "dooa&89I }}T__Q%78H H$4"--	277:;Ex055a89D,CKA:$!==.h'<3t9q= ab f+'-F6G#$$	"!d&7&78!d&7&78 M$9"=1	"=1		 1 y>Q#A,CK'lC
OaKF y>Q#A,CKaKF$	"0IsFS
*4::y#zRRr&   c                t   t        | t              r7| j                  dk(  rdt        | j                        dk\  rK|j                  | j                  d         }|+t        t        j                  |      j                  d         S y| j                  dk(  rt        | j                        dk(  r|j                  | j                  d         }|j                  | j                  d         }||yt        t        j                  |      j                  d         }t        t        j                  |      j                  d         }||z   S yt        | t              r@|j                  | j                        }|yt        |      }|t        |      dk\  r|d   S dS t        | t              r@|j                  | j                        }|yt        |      }|t        |      dk\  r|d   S dS t        | t              r@|j                  | j                        }|yt        |      }|t        |      dk\  r|d   S dS y)ar  Get the t-component dual (lambda) from the root cone.

    Parameters
    ----------
    tree : DecompositionTree
        The decomposition tree structure.
    dual_vars : Dict[int, Any]
        Mapping from constraint ID to dual variable value.

    Returns
    -------
    Optional[float]
        The dual value for t from the root cone, or None if not available.
    r   r   NrJ   )rK   r   r   rL   r    r|   r}   rM   rN   r~   r)   r*   rV   r.   r0   r3   )r[   r   r   r   r   r   r   r   s           r'   _get_root_t_dualr     s    $
#!4??#q($==);<' x!8!=!=a!@AA!#DOO(<(A "dooa&89I }}T__Q%78H H$4"--	277:;Ex055a89D4<$!==.h'*s4yA~tAwG4G$	"==!2!23h'*s4yA~tAwG4G$	"==!2!23h'*s4yA~tAwG4Gr&   c                    t        | |      }|y| j                  dz
  }|dk(  rt        j                  |g      S t        j                  |      }t        | ||d      }|yt        j                  |g|g      S )a  Reconstruct the original SOC dual from decomposed cone duals.

    For an SOC constraint ||x|| <= t, the dual is a flat array [lambda, mu1, mu2, ...]
    where lambda is the dual for t and mu is the dual for x.

    This function reconstructs the original dual by:
    1. Getting lambda from the root cone
    2. Collecting all mu components from leaf cones in order

    Parameters
    ----------
    tree : DecompositionTree
        The decomposition tree structure.
    dual_vars : Dict[int, Any]
        Mapping from constraint ID to dual variable value (flat arrays).

    Returns
    -------
    Optional[np.ndarray]
        Reconstructed dual as flat array [lambda, mu1, mu2, ...] or None if failed.
    Nr   r   )r   r   rM   arrayemptyr   rP   )r[   r   t_dualx_sizex_dualsfinal_offsets         r'   _reconstruct_soc_dualr   5  s    2 dI.F~ "F{xx!! hhvG.tYKL>>F8W-..r&   c                  4    e Zd ZdZddZddZ	 	 	 	 	 	 ddZy)	SOCDim3a$  Convert n-dimensional SOC constraints to dimension-3 SOC constraints.

    This reduction enables solvers that only support 3D SOC to handle
    arbitrary dimensional SOC constraints.

    The decomposition uses a binary tree structure:

    - Dimension 1 (||[]|| <= t): Convert to NonNeg(t)
    - Dimension 2 (|x| <= t): Pad with auxiliary variable to 3D
    - Dimension 3: Pass through unchanged
    - Dimension 4: Special chain structure (two 3D cones)
    - Dimension n > 4: Recursively split into tree of 3D cones

    Example
    -------
    >>> x = cp.Variable(4)
    >>> t = cp.Variable(nonneg=True)
    >>> prob = cp.Problem(cp.Minimize(t), [cp.norm(x, 2) <= t])
    >>> reduction = SOCDim3()
    >>> new_prob, inv_data = reduction.apply(prob)
    >>> # new_prob has only 3D SOC constraints
    c                     y)a  Check if this reduction accepts the given problem.

        This reduction accepts any problem.

        Parameters
        ----------
        problem : Problem
            The optimization problem.

        Returns
        -------
        bool
            Always True.
        Tr   )selfproblems     r'   acceptszSOCDim3.accepts}  s     r&   c                *   t        i |j                        }t        d |j                  D              }|s||fS g }|j                  D ]  }t        |t              r{|j
                  d   }|j
                  d   }|j                  }|dk(  r|j                  }t        |j                        dk(  r&t        ||j                  dfd      }	t        |      }
n|}	|}
|
j                  }|	j                  d   |k7  r$|dkD  rt        d| d|	j                  d    d	      g }g }g }t        |      D ]G  }|dkD  r|
|   n|
d   }|dkD  r	|	d
d
|f   n|	d
d
df   }t        ||||      }|j!                  |       I |j#                  |       |j#                  |       |j%                         }t'        |||r|d   nd|      |j(                  |j*                  <   |j!                  |         t-        j.                         |j0                  |      }||fS )a  Apply SOCDim3 decomposition to all SOC constraints.

        Parameters
        ----------
        problem : Problem
            The optimization problem to transform.

        Returns
        -------
        new_problem : Problem
            Problem with all SOC constraints decomposed to dim-3.
        inverse_data : SOCDim3InverseData
            Data needed to reconstruct original dual variables.
        )r?   r@   c              3  <   K   | ]  }t        |t                y w)N)rK   r   ).0ri   s     r'   	<genexpr>z SOCDim3.apply.<locals>.<genexpr>  s     FQjC(Fs   r   r   rD   rE   zDimension mismatch: t has z elements but X has z columnsNr+   )r9   r:   r   r;   )r=   constraintsanyrK   r   argsr;   TrL   shaper   r_   rH   r^   rangerc   r`   rb   
cone_sizesr8   r?   ra   r   r   	objective)r   r   inverse_datahas_socnew_constraintscontXr;   
X_reshaped
t_reshapedr:   	all_treesrf   rg   it_ix_ir[   r   new_problems                        r'   applyzSOCDim3.apply  s9    *#//
 F'2E2EFFL(( "&& 7	,C#s#HHQKHHQKxx 19A qww<1$!(QVVQKs!CJ!1!!4J!"J!"J&OO	 ##A&)3	A$4YK @!!+!1!1!!4 5X? 
 68	&(+-
 y) +A+4q=*Q-jmC.7!m*QT*AqDAQC0c8ZPD$$T*+  &&x0&&z2 !^^-
1<#'2<A!	2&&svv.  &&s+o7	,r )h&&():):OLL((r&   c           	     D   |j                   r|j                   j                         ni }|j                  s-t        |j                  |j
                  |i |j                        S i }t               }|j                  j                         D ]-  }|j                  D ]  }|j                  t        |              / |j                  j                         D ]  \  }}	||vs|	||<    |j                  j                         D ]  \  }
}|j                  }t        |      dk(  r"t        |d   |j                        }|<|||
<   Bg }d}|D ]/  }t        ||j                        }|d} n|j!                  |       1 |s}t#        j$                  |D cg c]  }|d   	 c}      }t#        j&                  |D cg c]  }|dd 	 c}      }|j(                  dk(  r|j*                  }||g||
<    t        |j                  |j
                  |||j                        S c c}w c c}w )a  Reconstruct solution with original SOC dual variables.

        Parameters
        ----------
        solution : Solution
            Solution from the decomposed problem.
        inverse_data : SOCDim3InverseData
            Data from apply() containing tree structures.

        Returns
        -------
        Solution
            Solution with reconstructed dual variables.
        r   r   NTF)primal_varscopyr   r   statusopt_valattrrX   r?   valuesr9   rY   rZ   itemsrL   r   r`   rM   r   column_stackr;   r   )r   solutionr   pvarsdvarsdecomposed_cone_ids	tree_datar[   cidr   orig_idr9   reconstructed	all_dualssuccessdt_dualsr   s                     r'   invertzSOCDim3.invert  s   ( 08/C/C$$))+ !!HOOX-=-=ub(--XX !# $'5%//668 	JI! J#**+B4+HIJ	J
 "++113 	"IC--!c
	"
 #/"8"8">">"@ 	8GYOOE5zQ 5eAh@R@R S ,%2E'N /1	! 4D$9$@R@R$SM$,"'$$]34  !hhi'@!'@AG ooi.Hqu.HIG ~~*")))&-w%7E'N7	8: )9)95%WW (A.Hs   H
4H
N)r   r   returnbool)r   r   r   z"Tuple[Problem, SOCDim3InverseData])r   r   r   r=   r   r   )r!   r"   r#   r$   r   r   r   r   r&   r'   r   r   e  s<    ."U)nFXFX )FX 
	FXr&   r   )rG   r   r   r   )rQ   zOptional[Any]r   Optional[np.ndarray])r[   r4   r   rX   )
rd   r   re   r   rf   z	List[SOC]rg   zList[NonNeg]r   r4   )
r[   r4   r   Dict[int, Any]r   z
np.ndarrayr   r   r   zOptional[int])r[   r4   r   r   r   zOptional[float])r[   r4   r   r   r   r   )3r$   
__future__r   dataclassesr   r   typingr   r   r   r	   r
   r   r   cvxpy.problems.problemr   numpyrM   cvxpy.atoms.affine.reshaper   cvxpy.atoms.affine.vstackr   cvxpy.constraints.nonposr   cvxpy.constraints.second_orderr   cvxpy.expressionsr   cvxpy.expressions.expressionr   cvxpy.expressions.variabler   cvxpy.reductions.reductionr   cvxpy.reductions.solutionr   r   r)   r.   r3   r4   r8   r=   rH   rV   rZ   rc   r   r   r   r   r   r&   r'   <module>r      s  %N # ( I I I.  . , + . & 3 / 0 . $  & $    $  . $  4 *h	9DE    ( 8 8 8$*  %F>ppp p 	p
 pnd
dd 
d 	d
 dN;|)/
)/)/ )/`FXi FXr&   