<jupyter_start><jupyter_text>Phase rotation of bidiagonal matrixThe bidiagnoal matrix produced by `B=bidiag(A)` might be complex and might have negative numbers on the main diagonal. It is possible to phase shift the matrix so that all the entries on the main diagonal are positive (or zero) and the bidiagonal has only real entries.    $ A \rightarrow U_{complex} B_{complex} {V^H}_{complex} \rightarrow {U^{'}}_{complex} {B^{'}}_{real} {{V^{'}}^H}_{complex} $  *Preliminaries*   *This document describes one of the algorithms needed for doing the singular value decomposition.*   *The environment needed to run the example includes the pyJvsip module and selected other algorithms which may be imported from the decompostionUtilities module. The decompositionUtilities module should reside in the same directory as this notebook and includes algorithms developed for the matrix decomposition algorithms (including biDiagPhaseToZero) as a collection.*  Bidiagonal Matrix Phase To ZeroIn the SVD calculation function `biDiagPhaseToZero` is a decomposition routine which is used to convert an upper bidiagonal matrix, which may be real or complex into a real, upper bidiagonal matrix. The entries can also be made $\ge 0$ if that is desired.   Entries are also checked to see if they are zero. Zero checking is a necessary function in the iteration to decide when a value is small enough to be considerd zero. When this decision is made the value is set exactly to zero so it may be effectively checked logically. The decomposition operation here is the same as everywhere else. Note we assume we are in a process that started with $A = L B R$ which is maintained by accumulating decomposition steps into the left and right matrices. Also note that $\hat{d_i} = d_i/{\left|d_i\right|}$ and $\hat{d_i} \hat{\bar{d_i}} = 1.0$ which allows us to represent the identity matrix as   $\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}= \begin{bmatrix}\hat{\bar{d_0}}&0&0&0\\\\0&\hat{\bar{d_1}}&0&0\\\\0&0&\hat{\bar{d_2}}&0\\\\0&0&0&\hat{\bar{d_3}}\end{bmatrix}\begin{bmatrix}\hat{d_0}&0&0&0     \\\\0&\hat{d_1}&0&0\\\\0&0&\hat{d_2}&0\\\\0&0&0&\hat{d_3}\end{bmatrix}$ Note we are working a size 4 example case here but the results are easily extended to any size bidiagonal matrix.We see that  $\begin{bmatrix} d_0 & f_0 & 0 & 0\\\\0 & d_1 & f_1 & 0\\\\0 & 0 & d_2 & f_2\\\\0 & 0 & 0 & d_3\end{bmatrix}\rightarrow\begin{bmatrix}\hat{\bar{d_0}}&0&0&0\\\\0&\hat{\bar{d_1}}&0&0\\\\0&0&\hat{\bar{d_2}}&0\\\\0&0&0&\hat{\bar{d_3}}\end{bmatrix}\begin{bmatrix}\hat{d_0}&0&0&0     \\\\0&\hat{d_1}&0&0\\\\0&0&\hat{d_2}&0\\\\0&0&0&\hat{d_3}\end{bmatrix}\begin{bmatrix} d_0 & f_0 & 0 & 0\\\\0 & d_1 & f_1 & 0\\\\0 & 0 & d_2 & f_2\\\\0 & 0 & 0 & d_3\end{bmatrix}\rightarrow$   $\begin{bmatrix}\hat{\bar{d_0}}&0&0&0\\\\0&\hat{\bar{d_1}}&0&0\\\\0&0&\hat{\bar{d_2}}&0\\\\0&0&0&\hat{\bar{d_3}}\end{bmatrix}\begin{bmatrix} \left|d_0\right| & \hat{d_0}f_0 & 0 & 0\\\\0 & \left|d_1\right| & \hat{d_1}f_1 & 0\\\\0 & 0 & \left|d_2\right| & \hat{d_2}f_2\\\\0 & 0 & 0 & \left|d_3\right|\end{bmatrix}\rightarrow\begin{bmatrix}\hat{\bar{d_0}}&0&0&0\\\\0&\hat{\bar{d_1}}&0&0\\\\0&0&\hat{\bar{d_2}}&0\\\\0&0&0&\hat{\bar{d_3}}\end{bmatrix}\begin{bmatrix} \left|d_0\right| & f{_0^{'}} & 0 & 0\\\\0 & \left|d_1\right| & f{_1^{'}} & 0\\\\0 & 0 & \left|d_2\right| & f{_2^{'}}\\\\0 & 0 & 0 & \left|d_3\right|\end{bmatrix}$ So we see that the main diagonal is real and positive but the super diagonal is not determined as real or positive, and each entry in the super diagonal has undergone some phase shift. We note that $\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix} \left|d_0\right| & f{_0^{'}} & 0 & 0\\\\0 & \left|d_1\right| & f{_1^{'}} & 0\\\\0 & 0 & \left|d_2\right| & f{_2^{'}}\\\\0 & 0 & 0 & \left|d_3\right|\end{bmatrix}\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\rightarrow$     $\begin{bmatrix} 1&0&0&0\\\\0&\hat{f{_0^{'}}}&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix}1 &0&0&0\\\\0&\hat{\bar{f{_0^{'}}}}&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix} \left|d_0\right| & f{_0^{'}} & 0 & 0\\\\0 & \left|d_1\right| & f{_1^{'}} & 0\\\\0 & 0 & \left|d_2\right| & f{_2^{'}}\\\\0 & 0 & 0 & \left|d_3\right|\end{bmatrix}\begin{bmatrix} 1&0&0&0\\\\0&\hat{f{_0^{'}}}&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix} 1&0&0&0\\\\0&\hat{\bar{f{_0^{'}}}}&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\rightarrow$ $\begin{bmatrix} 1 &0&0&0\\\\0&\hat{f{_0^{'}}}&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix} \left| d_0\right| &  f{_0^{'}} \hat{f{_0^{{'}{'}}}} & 0 & 0 \\\\ 0 & \hat{\bar{f{_0^{'}}}} \left| d_1\right| \hat{f{_0^{'}}} & f{_1^{'}}\hat{f{_0^{'}}} & 0\\\\0 & 0 & \left|d_2\right| & f{_2^{'}}\\\\0 & 0 & 0 & \left|  d_3 \right| \end{bmatrix}\begin{bmatrix}1 &0&0&0\\\\0&\hat{\bar{f{_0^{'}}}}&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\rightarrow \begin{bmatrix} 1 &0&0&0\\\\0&\hat{f{_0^{'}}}&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix} \left|d_0\right| & \left| f{_0^{'}} \right| & 0 & 0\\\\0 & \left|d_1\right|& f{_1^{{'}{'}}} & 0\\\\0 & 0 & \left|d_2\right| & f{_2^{'}}\\\\0 & 0 & 0 & \left|d_3\right|\end{bmatrix}\begin{bmatrix}1 &0&0&0\\\\0&\hat{\bar{f{_0^{'}}}}&0&0\\\\0&0&1&0\\\\0&0&0&1\end{bmatrix}$  So we have made $f_0$ entry positive without affecting the main diagonal. We now work on the next entry in the super diagonal   $\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&\hat{{f{_1^{''}}}}&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix}1 &0&0&0\\\\0&1&0&0\\\\0&0&\hat{\bar{f{_1^{''}}}}&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix}\left|d_0\right| & \left| f{_0^{'}} \right| & 0 & 0 \\\\0 & \left| d_1 \right| & f{_1^{''}} & 0\\\\0 & 0 & \left|d_2\right| & f{_2^{'}}\\\\0 & 0 & 0 & \left| d_3 \right| \end{bmatrix}\begin{bmatrix}1 &0&0&0\\\\0&1&0&0\\\\0&0&\hat{f{_1^{''}}}&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix} 1 &0&0&0\\\\0&1&0&0\\\\0&0&\hat{\bar{f{_1^{''}}}}&0\\\\0&0&0&1\end{bmatrix}\rightarrow$ $\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&\hat{{f{_1^{''}}}}&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix} \left|d_0\right| & \left| f{_0^{'}} \right| & 0 & 0 \\\\ 0 & \left| d_1 \right| & f{_1^{''}}\hat{f{_1^{''}}} & 0\\\\0 & 0 & \hat{\bar{f{_1^{''}}}} \left|d_2\right|\hat{f{_1^{''}}} & \hat{\bar{f{_1^{''}}}}f{_2^{'}}\\\\0 & 0 & 0 & \left| d_3 \right| \end{bmatrix}\begin{bmatrix} 1 &0&0&0\\\\0&1&0&0\\\\0&0&\hat{\bar{f{_1^{''}}}}&0\\\\0&0&0&1\end{bmatrix}\rightarrow\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&\hat{{f{_1^{''}}}}&0\\\\0&0&0&1\end{bmatrix}\begin{bmatrix}\left|d_0\right| & \left| f{_0^{'}} \right| & 0 & 0 \\\\0 & \left| d_1 \right| & \left|f{_1^{''}}\right| & 0\\\\0 & 0 &  \left|d_2\right| & f{_2^{''}}\\\\0 & 0 & 0 & \left| d_3 \right| \end{bmatrix}\begin{bmatrix} 1 &0&0&0\\\\0&1&0&0\\\\0&0&\hat{\bar{f{_1^{''}}}}&0\\\\0&0&0&1\end{bmatrix}$ So we have made $f_1$ and $f_0$ entry positive without affecting the main diagonal. We now work on the last entry in the super diagonal   $\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&\hat{{f{_2^{''}}}}\end{bmatrix}\begin{bmatrix}1 &0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&\hat{\bar{f{_2^{''}}}}\end{bmatrix}\begin{bmatrix}\left|d_0\right| & \left| f{_0^{'}} \right| & 0 & 0 \\\\0 & \left| d_1 \right| & \left|f{_1^{''}}\right| & 0\\\\0 & 0 &  \left|d_2\right| & f{_2^{''}}\\\\0 & 0 & 0 & \left| d_3 \right| \end{bmatrix}\begin{bmatrix}1 &0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&\hat{f{_2^{''}}}\end{bmatrix}\begin{bmatrix} 1 &0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&\hat{\bar{f{_2^{''}}}}\end{bmatrix}\rightarrow$ $\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&\hat{{f{_2^{''}}}}\end{bmatrix}\begin{bmatrix}\left|d_0\right| & \left| f{_0^{'}} \right| & 0 & 0 \\\\0 & \left| d_1 \right| & \left|f{_1^{''}}\right| & 0\\\\0 & 0 &  \left|d_2\right| & f{_2^{''}}\hat{{f{_2^{''}}}}\\\\0 & 0 & 0 & \hat{\bar{f{_2^{''}}}}\left| d_3 \right|\hat{{f{_2^{''}}}} \end{bmatrix}\begin{bmatrix} 1 &0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&\hat{\bar{f{_2^{''}}}}\end{bmatrix}\rightarrow\begin{bmatrix}1&0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&\hat{{f{_2^{''}}}}\end{bmatrix}\begin{bmatrix}\left|d_0\right| & \left| f{_0^{'}} \right| & 0 & 0 \\\\0 & \left| d_1 \right| & \left|f{_1^{''}}\right| & 0\\\\0 & 0 &  \left|d_2\right| & \left|f{_2^{''}}\right|\\\\0 & 0 & 0 & \left| d_3 \right| \end{bmatrix}\begin{bmatrix} 1 &0&0&0\\\\0&1&0&0\\\\0&0&1&0\\\\0&0&0&\hat{\bar{f{_2^{''}}}}\end{bmatrix}$ So we have succesfully decomposed the bidiagonal matrix so only the real part matters. This means the real SVD algorithm and the complex SVD algorithm from this point forward are the same. Note the left and right matrices may still be complex. Also note the left and right matrices above are assumed to be accumulated into any left and right matrix at each step so that $A = L B R$ is maintained. It is easy to see how the procedure would work for a matrix of any size. Below find a function to instantiate the above discussion. The matrices L and R are the left and right update matrices which are updated in the routine as the main and first diagonal (d,f) of the bidiagonal matrix are operated on. Following the function is an example to better illustrate the function use.<jupyter_code>import pyJvsip as pv
from decompositionUtilities import svdZeroCheckAndSet
def biDiagPhaseToZero(L,d,f,R,eps0):
    """ 
    For a Bidiagonal matrix B This routine uses subview vectors
      `d=B.diagview(0)`
    and 
      `f=B.diagview(1)`
    and phase shifts vectors d and f so that B has zero complex part.
    Matrices L and R are update matrices.
    eps0 is a small real number used to check for zero. If an element meets a zero
    check then that element is set to zero.
    """
    for i in range(d.length):
        ps=d[i]
        if ps.imag == 0.0:
            m = ps.real
            if m < 0.0:
                ps=-1.0
            else:
                ps= 1.0
            m = abs(m) 
        else:
            m=pv.vsip_hypot_d(ps.real,ps.imag)
            ps /= m
        if m > eps0:
            L.colview(i)[:] *= ps
            d[i] = m
            if i < f.length:
                f[i] *= ps.conjugate()
        else:
            d[i] = 0.0
    svdZeroCheckAndSet(eps0,d,f)          
    for i in range(f.length-1):
        j=i+1
        ps = f[i]
        if ps.imag == 0.0:
            m = ps.real
            if m < 0.0:
                ps=-1.0
            else:
                ps= 1.0
            m = abs(m) 
        else:
            m=pv.vsip_hypot_d(ps.real,ps.imag)
            ps /= m
        L.colview(j)[:] *= ps.conjugate()
        R.rowview(j)[:] *= ps
        f[i] = m;
        f[j] *= ps
    j=f.length
    i=j-1
    ps=f[i]
    if ps.imag == 0.0:
        m = ps.real
        if m < 0.0:
            ps=-1.0
        else:
            ps= 1.0
        m = abs(m) 
    else:
        m=pv.vsip_hypot_d(ps.real,ps.imag)
        ps /= m
    f[i]=m
    L.colview(j)[:] *= ps.conjugate()
    R.rowview(j)[:] *= ps<jupyter_output><empty_output><jupyter_text>Example  For the example we artificially create a complex matrix which is bidiagonal. The update matrices start out as identity matrices. For simplicity we use a square matrix. A bidiagonal matrix will always be square for the essential part.<jupyter_code>A=pv.create('cmview_d',5,5).fill(0.0)
A.diagview(0).randn(4)
A.diagview(1).randn(5)
A.mprint('%.3f') # print the matrix
L=A.empty.fill(0.0)
L.diagview(0)[:]=1.0
R=L.copy
Ad=A.diagview
eps0=1E-7
biDiagPhaseToZero(L,Ad(0),Ad(1),R,eps0)
# print the matrix to see it is all real and positive
A.mprint('%.3f') 
#reconstruct the matrix to see it is the same as we started with
L.prod(A).prod(R).mprint('%.3f')<jupyter_output>[-0.156-0.472i  0.617-0.147i  0.000+0.000i  0.000+0.000i  0.000+0.000i;
  0.000+0.000i -1.057-0.728i -0.109-0.068i  0.000+0.000i  0.000+0.000i;
  0.000+0.000i  0.000+0.000i  0.813+0.340i  0.021-0.137i  0.000+0.000i;
  0.000+0.000i  0.000+0.000i  0.000+0.000i  0.695+0.736i  0.514+0.308i;
  0.000+0.000i  0.000+0.000i  0.000+0.000i  0.000+0.000i  1.153-1.463i]
{'fraction_non_alphanumeric': 0.24694132334581773, 'fraction_numerical': 0.08572617561381607, 'mean_word_length': 3.073220338983051, 'pattern_counts': {'":': 0, '<': 12, '<?xml version=': 0, '>': 9, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '586988', 'license': 'MIT', 'n_tokens_mistral': 5488, 'n_tokens_neox': 4980, 'n_words': 1068, 'path': 'python/python_examples/matrixDecomposition/biDiagPhaseToZeroNB.ipynb', 'repo_name': 'rrjudd/jvsip', 'size': '12015'}