
    z	iG                        S r SSKJr  SSKrSSKJr  SSKJr  SSKrSSK	J
r
Jr  SSKrSSKJr  SS	KJrJr   " S
 S5      rS rS rSS jrg)zZ2Symmetries for SparsePauliOp.    )annotationsN)Iterable)deepcopy)Unioncast)QiskitError   )PauliSparsePauliOpc                      \ rS rSrSr SSS.         SS jjjr\SS j5       r\SS j5       r\SS	 j5       r	\SS
 j5       r
\SS j5       rS rSS jr\SS j5       rSS jrSS jrSS jrSS jrS S jrSrg)!Z2Symmetries   a  
The $Z_2$ symmetry converter identifies symmetries from the problem hamiltonian and uses them to
provide a tapered - more efficient - representation of operators as Paulis for this problem. For each
identified symmetry, one qubit can be eliminated in the Pauli representation at the cost of having to
test two symmetry sectors (for the two possible eigenvalues - tapering values - of the symmetry).
In certain problems such as the finding of the main operator's ground state, one can a priori
identify the symmetry sector of the solution and thus effectively reduce the computational overhead.

The following attributes can be read and updated once the ``Z2Symmetries`` object has been
constructed.

Attributes:
    tapering_values (list[int] or None): Values determining the sector.
    tol (float): The tolerance threshold for ignoring real and complex parts of a coefficient.

References:
    [1]: Bravyi, S., et al, "Tapering off qubits to simulate fermionic Hamiltonians"
        `arXiv:1701.08213 <https://arxiv.org/abs/1701.08213>`__
Ng+=)tolc                  [        U5      n[        U5      n[        U5      nUc  SO
[        U5      n[        U5      [        U5      :w  a$  [        S[        U5       S[        U5       S35      e[        U5      [        U5      :w  a$  [        S[        U5       S[        U5       S35      eUb<  [        U5      [        U5      :w  a$  [        S[        U5       S[        U5       S	35      eXl        X l        X0l        X@l        XPl        g)
a  
Args:
    symmetries: Object representing the list of $Z_2$ symmetries. These correspond to
        the generators of the symmetry group $\langle \tau_1, \tau_2\dots \rangle>$.
    sq_paulis: Object representing the list of single-qubit Pauli $\sigma^x_{q(i)}$
        anti-commuting with the symmetry $\tau_i$ and commuting with all the other symmetries
        $\tau_{j\neq i}$. These operators are used to construct the unitary Clifford operators.
    sq_list: The list of indices $q(i)$ of the single-qubit Pauli operators used to build the
        Clifford operators.
    tapering_values: List of eigenvalues determining the symmetry sector for each symmetry.
    tol: Tolerance threshold for ignoring real and complex parts of a coefficient.

Raises:
    QiskitError: Invalid paulis. The lists of symmetries, single-qubit paulis support paulis
        and tapering values must be of equal length. This length is the number of applied
        symmetries and translates directly to the number of eliminated qubits.
NzThe number of Z2 symmetries, zK, has to match the number                 of single-qubit pauli operators, .z,The number of single-qubit pauli operators, zG, has to match the length                 of the of single-qubit list, z%The length of the single-qubit list, zD, must match the length of the                     tapering values, z .)listlenr   _symmetries
_sq_paulis_sq_listtapering_valuesr   )self
symmetries	sq_paulissq_listr   r   s         d/home/james-whalen/.local/lib/python3.13/site-packages/qiskit/quantum_info/analysis/z2_symmetries.py__init__Z2Symmetries.__init__2   s   4 *%
O	w-"1"9$tO?Tz?c)n,/J/@ A225i.1AD 
 y>S\)>s9~>N O..1'l^1> 
 &7|s?33!;CL> J&&)/&:%;2? 
 &#.    c                    U R                   $ )zReturn symmetries.)r   r   s    r   r   Z2Symmetries.symmetriesj   s     r   c                    U R                   $ )zReturn sq paulis.)r   r!   s    r   r   Z2Symmetries.sq_pauliso   s     r   c                    [        U R                  U R                  5       VVs/ s H3  u  p[        U5      [        U5      -   [        R
                  " S5      -  PM5     nnnU$ s  snnf )z
Get clifford operators, built based on symmetries and single-qubit X.

Returns:
    A list of unitaries used to diagonalize the Hamiltonian.
r	   )zipr   r   r   mathsqrt)r   
pauli_symmsq_pauli	cliffordss       r   r+   Z2Symmetries.cliffordst   sa     ),D,<,<doo(N
(N$
 :&x)@@DIIaLP(N 	 
 	
s   :A#c                    U R                   $ )zReturn sq list.)r   r!   s    r   r   Z2Symmetries.sq_list   s     }}r   c                `    U R                   U R                  U R                  U R                  S.$ )zReturn operator settings.)r   r   r   r   )r   r   r   r   r!   s    r   settingsZ2Symmetries.settings   s.     **}}#33	
 	
r   c                   S/nUR                  S5        U R                   H"  nUR                  UR                  5       5        M$     UR                  S5        U R                   H"  nUR                  UR                  5       5        M$     UR                  S5        U R                   H  nUR                  [        U5      5        M     UR                  S5        UR                  [        U R                  5      5        UR                  S5        U R                  cy  [        R                  " SS/[        U R                  5      S	9 Vs/ s H  n[        [        U5      5      PM     nnS
R                  S U 5       5      nUR                  SU-   5        O$UR                  [        U R                  5      5        SR                  U5      nU$ s  snf )NzZ2 symmetries:zSymmetries:zSingle-Qubit Pauli X:z
Cliffords:zQubit index:zTapering values:   repeatz, c              3  $   #    U  H  ov   M     g 7fN ).0xs     r   	<genexpr>'Z2Symmetries.__str__.<locals>.<genexpr>   s     'C?a?s   z  - Possible values: 
)appendr   to_labelr   r+   strr   r   	itertoolsproductr   r   join)r   retsymmetryr;   ccoeffpossible_valuess          r   __str__Z2Symmetries.__str__   sf    

=!((HJJx((*+ )

*+AJJqzz|$ !

< AJJs1v  

>"

3t}}%&

%&'.7.?.?BPSTXTaTaPb.c.cUDK .c   #ii'C?'CCOJJ.@AJJs4//01iin
s   Gc                    [        U R                  5      S:H  =(       d7    [        U R                  5      S:H  =(       d    [        U R                  5      S:H  $ )zF
Check the z2_symmetries is empty or not.

Returns:
    Empty or not.
r   )r   r   r   r   r!   s    r   is_emptyZ2Symmetries.is_empty   sA     4##$)aS-AQ-Fa#dmmJ\`aJaar   c                  ^^^^ / n/ n/ n/ nSS/SS/SS/S.nSS/SS/SS/S.nSS/SS/SS/S.n[        U5      (       a  U " / / / S	5      $ [        U5       Hj  n	UR                  [        R                  " U	R
                  R                  S
   U	R
                  R                  S
   4S
S9R                  [        5      5        Ml     [        R                  " U5      n
[        U
5      nU(       d  U " / / / S	5      $ [        R                  " U5      mTR                  mTS   S-  m[        TS
   5       Vs/ s H  n[        R                  " TUS
S9PM     snmSUUUU4S jjn[        TS
   5       H  nUR                  [        TUS	T24   TUTS	24   45      5        [        T5       H  nS H  nU" XXo   X   5      nU(       d  M  UR                  [        [        R                   " T5      [        R                   " T5      45      5        X   S
   X<   R                  U'   X   S   X<   R                  U'   UR                  U5          O   W(       d  M    M     M     U " X#US	5      $ s  snf )zi
Finds Z2 Pauli-type symmetries of a :class:`.SparsePauliOp`.

Returns:
    A ``Z2Symmetries`` instance.
)r   r   )r3   r   )r3   r3   )r   r3   )X_or_IY_or_IZ_or_I)rR   rP   rQ   FTNr   axisr3   r	   c           
       > [         R                  " [        T
S   S-
  5       Vs/ s H  nTU    XA4   TU    XAT-   4   4U;   PM     sn5      nT	X4   T	XT-   4   4U;   n[        [         R                  " U5      5      =(       a    U$ s  snf )a~  
Utility method that determines how to build the list of single-qubit Pauli X operators and
the list of corresponding qubit indices from the stacked symmetries.
This method is successively applied to Z type, X type and Y type symmetries (in this order)
to build the letter at position (col) of the Pauli word corresponding to the symmetry at
position (row).

Args:
    row (int): Index of the symmetry for which the single-qubit Pauli X operator is being
        built.
    col (int): Index of the letter in the Pauli word corresponding to the single-qubit Pauli
        X operator.
    idx_test (list): List of possibilities for the stacked symmetries at all other rows
        than row.
    row_test (list): List of possibilities for the stacked symmetries at row.

Returns:
    Whether or not this symmetry type should be used to build this letter of this
    single-qubit Pauli X operator.
r   r3   )nparrayrangeboolall)rowcolidx_testrow_testsymm_idxstacked_symm_idx_testsstacked_symm_row_testhalf_symm_shapestacked_symm_delstacked_symmetries
symm_shapes          r   _test_symmetry_row_col?Z2Symmetries.find_z2_symmetries.<locals>._test_symmetry_row_col   s    * &(XX %**Q-!*;$< %=	 )-hm<(-ho8M.MN  	 
 %=	&" #38,"3o(=#=>% %!
 567Q<QQs   "B)
r[   intr\   rh   r]   r   r^   r   returnrY   )_sparse_pauli_op_is_zeroiterr?   rV   concatenatepaulisr;   zastyperh   stack
_kernel_f2shaperX   deleter
   zeros)clsoperatorpauli_symmetriesr   r   stacked_paulistest_idxtest_row
pauli_boolpaulistacked_matrixr   r[   rf   r\   keycurrent_test_resultrb   rc   rd   re   s                    @@@@r   find_z2_symmetriesZ2Symmetries.find_z2_symmetries   s    	 v&v&v&
 v&v&v&
 dmUmTl

 $H--r2r4(((^E!!q 15<<>>!3DEANUUVYZ $
 .1/
r2r4((XXj1'--
$Q-1,BG
STBV
BV3BII(#A6BV
%	R %	RN A'C##*30@0@+@A*30@+@A _-9C*@(-+' +*!((!288O#<bhh>W"XY 1;0B	((-0:0B	((-s+ : '& . (< #>>S
s   2 I&c                    U R                  5       (       dG  [        U5      (       d7  U R                   H'  n[        [        X!-  U-  5      nUR                  SS9nM)     U$ )a  This method operates the first part of the tapering.
It converts the operator by composing it with the clifford unitaries defined in the current
symmetry.

Args:
    operator: The to-be-tapered operator.

Returns:
    ``SparsePauliOp`` corresponding to the converted operator.

        atol)rM   rj   r+   r   r   simplify)r   rv   cliffords      r   convert_cliffordZ2Symmetries.convert_clifford/  sV     }}'?'I'I NNx/BX/MN#,,#,6 + r   c           	     <   U R                  5       (       a  UnU$ U R                  cU  [        R                  " SS/[	        U R
                  5      S9 Vs/ s H  nU R                  U[        U5      5      PM      nnU$ U R                  XR                  5      nU$ s  snf )a  Operate the second part of the tapering.
This function assumes that the input operators have already been transformed using
:meth:`convert_clifford`. The redundant qubits due to the symmetries are dropped and
replaced by their two possible eigenvalues.

Args:
    operator: Partially tapered operator resulting from a call to :meth:`convert_clifford`.

Returns:
    If tapering_values is None: [:class:`SparsePauliOp`]; otherwise, :class:`SparsePauliOp`.

r3   r4   r5   )rM   r   rB   rC   r   r   _taperr   )r   rv   tapered_opsrH   s       r   taper_cliffordZ2Symmetries.taper_cliffordD  s     ==??"K  ##+ "+!2!2Ar73t}}CU!V!V KK$u+6!V    #kk(4H4HIs   %Bc                J    U R                  U5      nU R                  U5      nU$ )ar  
Taper an operator based on the z2_symmetries info and sector defined by `tapering_values`.
Returns operator if the symmetry object is empty.

The tapering is a two-step algorithm which first converts the operator into a
:class:`SparsePauliOp` with same eigenvalues but where some qubits are only acted upon
with the Pauli operators I or X.
The number M of these redundant qubits is equal to the number M of identified symmetries.

The second step of the reduction consists in replacing these qubits with the possible
eigenvalues of the corresponding Pauli X, giving 2^M new operators with M less qubits.
If an eigenvalue sector was previously identified for the solution, then this reduces to
1 new operator with M less qubits.

Args:
    operator: The to-be-tapered operator.

Returns:
    If tapering_values is None: [:class:`SparsePauliOp`]; otherwise, :class:`SparsePauliOp`.

)r   r   )r   rv   converted_opsr   s       r   taperZ2Symmetries.taperc  s*    . --h7))-8r   c                X   / n[        U5       GHY  nUR                  S   n[        U R                  5       HN  u  pgUR                  R
                  SU4   (       d"  UR                  R                  SU4   (       d  MG  X&   U-  nMP     [        R                  " UR                  R
                  S   R                  5       [        R                  " U R                  5      5      n[        R                  " UR                  R                  S   R                  5       [        R                  " U R                  5      5      n	UR                  [        X45      R                  5       U45        GM\     [        R                  " U5      R!                  SS9n
U
R#                  U R$                  5      n
U
$ )Nr   r   r   )rk   coeffs	enumerater   rm   rn   r;   rV   rs   copyasarrayr?   r
   r@   r   	from_listr   chopr   )r   opcurr_tapering_values
pauli_list
pauli_term	coeff_outidx	qubit_idxz_tempx_tempspos              r   r   Z2Symmetries._taper  sD   
r(J"))!,I"+DMM":$$&&q)|4
8I8I8K8KAyL8Y8Y 4 9I EI #; YYz002215::<bjj>WXFYYz002215::<bjj>WXFuf%56??A9MN # %%j1:::Dhhtxx 
r   c                    [        U[        5      (       d  gU R                  UR                  :H  =(       aY    U R                  UR                  :H  =(       a9    U R                  UR                  :H  =(       a    U R
                  UR
                  :H  $ )z
Overload `==` operation to evaluate equality between Z2Symmetries.

Args:
    other: The `Z2Symmetries` to compare to self.

Returns:
    A bool equal to the equality of self and other.
F)
isinstancer   r   r   r   r   )r   others     r   __eq__Z2Symmetries.__eq__  sr     %.. OOu/// >%//1>-> $$(=(==		
r   )r   r   r   r   r   r8   )
r   Iterable[Pauli]r   r   r   zIterable[int]r   zIterable[int] | Noner   float)ri   zlist[Pauli])ri   zlist[SparsePauliOp])ri   	list[int])ri   dict)ri   rY   )rv   r   ri   r   )rv   r   ri   r   )rv   r   ri   z)Union[SparsePauliOp, list[SparsePauliOp]])r   r   r   r   ri   r   )r   r   ri   rY   )__name__
__module____qualname____firstlineno____doc__r   propertyr   r   r+   r   r0   rJ   rM   classmethodr   r   r   r   r   r   __static_attributes__r9   r   r   r   r      s    2 156 6#6 #6 	6
 .6 6p           
 
4b x? x?t*>8
r   r   c                &   U R                   n/ n[        R                  " U [        R                  " US   5      45      n[	        UR                  5       5      R                  5       n[        US   5       H  n[        R                  " USUS   2U4   [        R                  " US   5      5      (       d  MB  [        R                  " XAS   S2U4   [        R                  " US   5      5      (       a  M  UR                  XAS   S2U4   5        M     U$ )z
Compute the kernel of a binary matrix on the binary finite field.

Args:
    matrix_in (numpy.ndarray): Binary matrix.

Returns:
    The list of kernel vectors.
r3   r   N)
rr   rV   vstackidentity_row_echelon_f2	transposerX   array_equalrt   r?   )	matrix_insizekernelmatrix_in_idmatrix_in_id_echr\   s         r   rq   rq     s     ??DF99iT!W)=>?L'(>(>(@ALLNT!W~>>Qa[#-.a0A
 
..!1q')S.!A288DQRGCTUUMM*79c>:;	  Mr   c           	        U R                   n[        US   5       H|  nSn[        US   5       H  nXU4   S:X  d  M  Un  O   [        US   5       HB  nXR:w  d  M
  XU4   S:X  d  M  [        R                  " XSS24   XSS24   -   S5      XSS24'   MD     M~     [	        U 5      n/ n[        R
                  " U5      n[        US   S-
  5       HO  n[        R                  " XbSS24   [        R
                  " US   5      5      (       d  M>  UR                  U5        MQ     [        R                  " U5      SSS2    H  n	[        R                  " XiSS9nM     XhSUS   [        U5      -
  2SS24'   UR                  [        5      nU$ )z
Compute the row Echelon form of a binary matrix on the binary finite field.

Args:
    matrix_in (numpy.ndarray): Binary matrix.

Returns:
    Matrix_in in Echelon row form.
r   r3   Nr	   r4   rS   )rr   rX   rV   modr   rt   r   r?   sortrs   r   ro   rh   )
r   r   ipivot_indexjkmatrix_out_tempindices
matrix_outr[   s
             r   r   r     sf    ??D47^tAwAA!#   tAwAv){N3q8"$&&a49T?)JA"N	Q$    y)OG$J47Q;>>/Q$/$q'1BCCNN1   www"%))OC & 1@q47S\))1,-""3'Jr   c                    U R                  5       n [        U R                  5      S:H  =(       a    U R                  S   S:H  $ )zAReturns whether or not this operator represents a zero operation.r3   r   )r   r   r   )r   s    r   rj   rj     s2    	Bryy>Q4299Q<1#44r   )r   r   ri   rY   )r   
__future__r   rB   collections.abcr   r   r   r'   typingr   r   numpyrV   qiskit.exceptionsr   	operatorsr
   r   r   rq   r   rj   r9   r   r   <module>r      sB    & "  $     ) ,C
 C
L0#L5r   