Purely imaginary eigenvalues

Code
# initial usings
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
import math

# Define the matrix A
A = math.sqrt(2)/2  * np.array( [[1, -3, 2],
                                [1, -1, 1],
                                [0, 0, -1]])
# all text constants
scale = 1

q1 = np.array([2, 1.2]) * scale
q2 = -np.dot(A,np.array([*q1, 0]))[[0,1]]

# q1 = np.array([1, .8]) /1.2
# q2 = np.array([-2, 1])/2.2
# print(q1)
# print(q2)
#np.dot(A,np.array([*q2, 0]))[[0,1]]

Let A be a real matrix on \(\mathbb{R}^n\) 1. Let \(q\) be an eigenvector with a eigenvalue \(\lambda\) :

\[ q \in \mathbb{C}^n \setminus \{0\}, \lambda \in \mathbb{C}, \sigma,\omega \in \mathbb{R}, \omega \neq 0 \] \[ Aq=\lambda q, \quad \lambda = \sigma + i\omega \]

Without loss of generality (WLOG) \(\omega > 0\).2 For simplicity let \(\omega = 1\). Also let \(\sigma=0\), we will handle general cases at the end of this page.

For a nontrivial example, we choose \[ A_0 = \begin{pmatrix} 1 & -3 & 2 \\ 1 & -1 & 1 \\ 0 & 0 & -1 \end{pmatrix} \]

The eigenvalues of \(A_0\) are: \[ \lambda_{1,2} = \pm i\sqrt{2}, \quad \lambda_3 = -1 \]

The corresponding eigenvectors are: \[ q = \begin{pmatrix} 2+i\frac{\sqrt{2}}{2} \\ 1-i\frac{\sqrt{2}}{2} \\ 0 \end{pmatrix} \] There are simpler ones, we will see them later, this one is chosen for nice pictures.

Because we want \(\omega = 1\) we will take \(A = \frac{\sqrt{2}}{2} A_0\). The eigenvector \(q\) will be preserved this way and \(\lambda = i\).

Now we split \(q\) into a real and imaginary part:

\[ q = q_1 + iq_2 = Req + iImq\] Clearly \(q_1 \neq 0\) since LHS of the following equation would be real while the RHS would not. Similarly for \(q_2\).

\[Aq=A(q_1 + i q_2) = i(q_1 + i q_2) = -q_2 + iq_1 = Im\bar{q} + Re\bar{q}\]

This equation also implies \[ Aq_1 = -q_2,\; Aq_2=q_1\]

We can now see how A transforms the subspace generated by the real and imaginary part of vector q. Let us denote \(W = \mathop{\mathrm{span}}\{q_1, q_2\}\). These are surely linearly independent, so the dimension of \(W\) is 2.

\[ q_1 = \begin{pmatrix} 2 \\ 1 \\ 0 \end{pmatrix}, q_2 = \begin{pmatrix} \frac{\sqrt{2}}{2} \\ -\frac{\sqrt{2}}{2} \\ 0 \end{pmatrix} \]

Code
import matplotlib.pyplot as plt

# Create the figure and axis
fig, ax = plt.subplots(figsize=(6, 6))

# Set the axis limits
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)

# Add grid and axis labels
ax.axhline(0, color='black',linewidth=0.5)
ax.axvline(0, color='black',linewidth=0.5)
ax.set_xticks(np.arange(-2, 3, 1))
ax.set_yticks(np.arange(-2, 3, 1))
ax.grid(False)

# Plot the vectors
ax.quiver(0, 0, *q1, angles='xy', scale_units='xy', scale=1, color='blue', label=r'$q_1 = Req = Re\bar{q}$')
ax.quiver(0, 0, *q2, angles='xy', scale_units='xy', scale=1, color='green', label=r'$q_2 = Im q$')
ax.quiver(0, 0, *(-q2), angles='xy', scale_units='xy', scale=1, color='red', label=r'$-Im q = Im \bar{q}$')

# Add axis labels
ax.text(*q1, r'$q_1 = Req = Re\bar{q}$', fontsize=12, color='blue', verticalalignment='bottom', horizontalalignment='left')
ax.text(*q2, r'$q_2 = Im q$', fontsize=12, color='green', verticalalignment='top', horizontalalignment='right')
ax.text(*(-q2), r'$-Im q = Im \bar{q}$', fontsize=12, color='red', verticalalignment='bottom', horizontalalignment='right')

# Set labels for the axes (W)
ax.text(2, 0, 'W', fontsize=12, verticalalignment='bottom', horizontalalignment='left')

# Hide the grid and axes
ax.set_axis_off()

# Show the plot
plt.show()

Note that any vector from this subspace can be a real part of an eigenvector: For \(w_1 \in W\), define \(w_2 = -Aw_1\). One can verify that \(w = w_1 + iw_2\) satisfies \(Aw = iw\) making it an eigenvector with eigenvalue \(i\). We will focus on this subspace.

Moreover we can choose \(w_1 and w_2\) to be perpendicular (with respect to any inner product \(\langle\cdot,\cdot\rangle\)).

Proof:

If \(\langle q_1, q_2 \rangle = 0\), we are done. If not, WLOG \(\langle q_1, q_2 \rangle \gt 0\). But then

\[ \langle Aq_1, Aq_2 \rangle = \langle -q_2, q_1 \rangle = - \langle q_1, q_2 \rangle \lt 0 \] Define \[ w_1(t) = (1-t)q_1 + tq_2 \] \[ w_2(t) = -Aw_1(t) \]

These vectors are nonzero since \(q_1,q_2\) are linearly independent. Moreover

\[ \langle w_1(0), w_2(0) \rangle = \langle q_1, q_2 \rangle > 0 \] \[ \langle w_1(1), w_2(1) \rangle = \langle -Aq_1, -Aq_2 \rangle < 0 \]

and $w_1(t), w_2(t) and inner product are continuous functions, so there must exist a \(t\) for which

\[ \langle w_1(t), w_2(t) \rangle = 0 \]

\(\square\)

Note that to find the specific vectors one needs to solve the following quadratic equation: \[ % \langle w_1(t), w_2(t) \rangle = \langle q_1 + t(q_2-q_1) , q_2 + t(-q_1-q_2) \rangle = \langle q_1 + t(q_2-q_1) , q_2 + t(-q_1-q_2) \rangle = 0 \langle w_1(t), w_2(t) \rangle = \langle q_1 + t(q_2-q_1) , q_2 + t(-q_1-q_2) \rangle = \] \[ =\langle q_1, q_2 \rangle + t(\langle q_2, q_2 \rangle - \langle q_1, q_1 \rangle - 2\langle q_1, q_2 \rangle)+ t^2(\langle q_1, q_1 \rangle - \langle q_2, q_2 \rangle) = 0 \]

In the corollary, we see from this equation

\[ \langle q_1, q_2 \rangle = 0\]

\[ \lVert q_1 \rVert = \lVert q_2 \rVert\]

then for all cuples \(w_1, w_2\) constructed in the way described above

\[ \langle w_1, w_2 \rangle = 0 \] \[ \lVert w_1 \rVert = \lVert w_2 \rVert \]

Code
# Create the figure and axis
fig, ax = plt.subplots(figsize=(3, 3))

# Set the axis limits
ax.set_xlim(-1.5, 1.5)
ax.set_ylim(-1.5, 1.5)

# Add grid and axis labels
ax.axhline(0, color='black',linewidth=0.5)
ax.axvline(0, color='black',linewidth=0.5)
ax.set_xticks(np.arange(-2, 3, 1))
ax.set_yticks(np.arange(-2, 3, 1))
ax.grid(False)

# Plot the vectors
ax.quiver(0, 0, *q1, angles='xy', scale_units='xy', scale=1, color='blue', label=r'$q_1$')
ax.quiver(0, 0, *q2, angles='xy', scale_units='xy', scale=1, color='green', label=r'$q_2$')
# Draw a line from q1 to q2
ax.plot([q1[0], q2[0]], [q1[1], q2[1]], color='purple', linestyle='--', linewidth=1, label='Line from $q_1$ to $q_2$')

ax.quiver(0, 0, *(-q1), angles='xy', scale_units='xy', scale=1, color='blue', label=r'$q_2$')

# Calculate the dot products
dot_q1_q2 = np.dot(q1, q2)
dot_q1_q1 = np.dot(q1, q1)
dot_q2_q2 = np.dot(q2, q2)

# Coefficients of the quadratic equation
a = dot_q2_q2-dot_q1_q1
b = dot_q1_q1 - dot_q2_q2 + 2*dot_q1_q2
c = -dot_q1_q2

# Solve the quadratic equation
t1, t2 = np.roots([a,b,c])

# Select the t between 0 and 1
t = t1 if 0 <= t1 <= 1 else t2

w1 = (1-t)*q1 + t*q2  
w2 = -np.dot(A,np.array([*w1, 0]))[[0,1]]

ax.quiver(0, 0, *w1, angles='xy', scale_units='xy', scale=1, color='red', label=r'$w_1$')
ax.quiver(0, 0, *w2, angles='xy', scale_units='xy', scale=1, color='red', label=r'$w_1$')

ax.text(*w1, r'$w_1$', fontsize=12, color='red', verticalalignment='top', horizontalalignment='left')
ax.text(*w2, r'$w_2$', fontsize=12, color='red', verticalalignment='top', horizontalalignment='left')

ax.plot([q2[0], -q1[0]], [q2[1], -q1[1]], color='orange', linestyle='--', linewidth=1, label='Line from $q_1$ to $-q_1$')

# ax.quiver(0, 0, *(-q2), angles='xy', scale_units='xy', scale=1, color='red', label=r'$-Im q = Im \bar{q}$')

# Add axis labels
ax.text(*q1, r'$q_1$', fontsize=12, color='blue', verticalalignment='bottom', horizontalalignment='left')
ax.text(*q2, r'$q_2$', fontsize=12, color='green', verticalalignment='top', horizontalalignment='right')

# Set labels for the axes (W)
ax.text(2, 0, 'W', fontsize=12, verticalalignment='bottom', horizontalalignment='left')

# Hide the grid and axes
ax.set_axis_off()

# Show the plot
plt.show()

It would definitely be nicer to find a transformation following an elipse instead of straight line.

One application of this is in (linear) differential equations. Proven by picture we see that this ellipse can be found as a solution to \[ \dot{x} = Ax \]

Code
# Define the differential equation x' = Ax
def diff_eq(t, x):
    return np.dot(A, x)

# Initial condition
x0 = np.array([*q1, 0])

# Time span for the solution
t_span = (0, 10)
t_eval = np.linspace(t_span[0], t_span[1], 400)

# Solve the differential equation
sol = solve_ivp(diff_eq, t_span, x0, t_eval=t_eval)


# Create the figure and axis
fig, ax = plt.subplots(figsize=(6, 6))

# Set the axis limits
ax.set_xlim(-2.2, 2.2)
ax.set_ylim(-2.2, 2.2)

# Add grid and axis labels
ax.axhline(0, color='black',linewidth=0.5)
ax.axvline(0, color='black',linewidth=0.5)
ax.set_xticks(np.arange(-2, 3, 1))
ax.set_yticks(np.arange(-2, 3, 1))
ax.grid(False)

# Plot the vectors
ax.quiver(0, 0, *q1, angles='xy', scale_units='xy', scale=1, color='blue', label=r'$q_1$')
ax.quiver(0, 0, *q2, angles='xy', scale_units='xy', scale=1, color='green', label=r'$q_2$')
# Draw a line from q1 to q2
ax.plot([q1[0], q2[0]], [q1[1], q2[1]], color='black', linestyle='--', linewidth=1, label='Line from $q_1$ to $q_2$')
ax.plot([q2[0], -q1[0]], [q2[1], -q1[1]], color='black', linestyle='--', linewidth=1, label='Line from $q_1$ to $-q_1$')

ax.quiver(0, 0, *(-q1), angles='xy', scale_units='xy', scale=1, color='blue', label=r'$q_2$')

ax.quiver(0, 0, *w1, angles='xy', scale_units='xy', scale=1, color='red', label=r'$w_1$')
ax.quiver(0, 0, *w2, angles='xy', scale_units='xy', scale=1, color='red', label=r'$w_1$')


# Plot the solution of the differential equation
ax.plot(sol.y[0], sol.y[1], color='purple', label='Trajectory of $x(t)$')

# Add labels for the trajectory
# ax.text(sol.y[0][-1], sol.y[1][-1], r'$x(t)$', fontsize=12, color='orange', verticalalignment='bottom', horizontalalignment='left')
# ax.quiver(0, 0, *(-q2), angles='xy', scale_units='xy', scale=1, color='red', label=r'$-Im q = Im \bar{q}$')

# Add axis labels
ax.text(*q1, r'$q_1$', fontsize=12, color='blue', verticalalignment='bottom', horizontalalignment='left')
ax.text(*q2, r'$q_2$', fontsize=12, color='green', verticalalignment='top', horizontalalignment='right')



# Set labels for the axes (W)
ax.text(2, 0, 'W', fontsize=12, verticalalignment='bottom', horizontalalignment='left')

# Hide the grid and axes
ax.set_axis_off()

# Show the plot
plt.show()

Now we will build a one-to-one correspondence between this two dimensional subspace \(W\) and \(\mathbb{C}\). Of course there are multiple options how to choose a linear isomorphism between \(W\) and \(\mathbb{C}\), but we will see that each one corresponds to a choice of \(w_1\).

Lets consider the dual basis of \([q_1,q_2]\) in \(W^*\) (space of linear forms on \(W\)) and denote it \([p^1, p^2]\).3 4

\[p^1q_1 = 1 \;\;\;\; p^2q_1 = 0\] \[p^1q_2 = 0 \;\;\;\; p^2q_2 = 1\]

We can compose them into complex dual vector \(p=\frac{1}{2}(p^1-ip^2)\) so \(p, \bar{p}\) form a basis of complexified vector space \((W^*)^{\mathbb{C}} = (W^{\mathbb{C}})^*\) dual to \(q,\bar{q}\).

\[ q_1 = \begin{pmatrix} 2 \\ 1 \\ 0 \end{pmatrix}, q_2 = \begin{pmatrix} \frac{\sqrt{2}}{2} \\ -\frac{\sqrt{2}}{2} \\ 0 \end{pmatrix} \]

For this example we are lucky, that \(W\) is just standard \(\mathbb{R}^2\) and we get the dual basis of \(W\) as dual basis of \[ q_1 = \begin{pmatrix} 2 \\ 1 \end{pmatrix}, q_2 = \begin{pmatrix} \frac{\sqrt{2}}{2} \\ -\frac{\sqrt{2}}{2} \end{pmatrix} \] \[ Q=\begin{pmatrix} q_1 & q_2 \end{pmatrix} = \begin{pmatrix} 2 & \frac{\sqrt{2}}{2} \\ 1 & -\frac{\sqrt{2}}{2} \end{pmatrix} \]

in \(\mathbb{R}^2\).

\[ \begin{pmatrix} p^1 \\ p^2 \end{pmatrix} \begin{pmatrix} q_1 & q_2 \end{pmatrix}= \begin{pmatrix} 1 & 0 \\ 0 & 1 \end{pmatrix} \]

\[ \begin{pmatrix} p^1 \\ p^2 \end{pmatrix}= Q^{-1}= \frac{1}{3}\begin{pmatrix} 1 & 1 \\ \sqrt{2} & -2\sqrt{2} \end{pmatrix} \]

\[ p = \frac{1}{6} \begin{pmatrix} 1 - i\sqrt{2} & 1 + 2i\sqrt{2} \end{pmatrix} \]

Note: If the subspace \(W\) were different one can just add some other vectors to form a basis, compute an inverse and take the first two rows or come with some more clever ideas.

Lets now check if \(p\) has an inverse. By an inverse I mean inverse of the linear map5

\[ p:W \to \mathbb{C} \]

Where we view \(\mathbb{C}\) as vector space over \(\mathbb{R}\) (with some additional action \(\cdot\) of \(\mathbb{C} \setminus \{0\}\) which will prove useful later).

One can see that \(pq_1=\frac{1}{2}\), \(pq_2=\frac{1}{2}i\), which are linearly independent and since \(W\) and \(\mathbb{C}\) have both dimension 2, so \(p\) must be invertable. Particularly for

\[ w=aq_1 + bq_2 \in W \] \[ pw=(p^1+ip^2)(aq_1 + bq_2)=a+bi \]

In other words the matrix \[ \begin{pmatrix} p^1\\ p^2 \end{pmatrix} \]

is the matrix of linear isomorphism \(p\) between \(W\) and \(C\) in the bases \([q_1,q_2]\) and \([1,i]\).

For later use we can derive an elegant explicit form for the inverse:6

Let \[ z=a+bi \] Then \[ zp + \bar{z}\bar{p} = (a+bi)(q_1+iq_2)+(a-bi)(q_1-iq_2) = 2aq_1 + 2ibq_2 = 2p^{-1}(z) \] \[ p^{-1}(z) = \frac{1}{2} (zp + \bar{z}\bar{p}) \]

Lets now build a geometric intuition on these linear forms. \(p\) is complex, so it is better to work with its components \(p_1\) and \(p_2\). I like to vizualize them like functions of two variables in \(\mathbb{R}^3\).

Code
import plotly.graph_objects as go

def create_3d_plot(q1,q2,swap=False):
    q_ = q2 if swap else q1
    q = q1 if swap else q2
    p1 = np.array([q_[1], -q_[0]])
    coef = 1 / np.dot(q, p1)
    p1 = p1 * coef

    # Define hyperplane as a mesh grid
    x = np.array([q_ + p1, q_ - p1, -q_ + p1, -q_ - p1])
    z = np.dot(x, p1)
    z = np.reshape(z, (2, 2))
    x = np.reshape(x, (2, 2, 2))

    # Prepare 3D mesh coordinates for Plotly surface
    x_vals = x[:, :, 0]
    y_vals = x[:, :, 1]
    z_vals = z

    # Create a plotly figure
    fig = go.Figure()

    # Plot the hyperplane surface
    fig.add_trace(go.Surface(
        x=x_vals, 
        y=y_vals, 
        z=z_vals, 
        colorscale=[[0, 'lightblue'], [1, 'lightblue']], 
        opacity=0.5,
        showscale=False
    ))

    # Plot boundary edges of the surface
    for i in range(x.shape[0]):
        fig.add_trace(go.Scatter3d(
            x=x[i, :, 0], y=x[i, :, 1], z=z[i, :], 
            mode='lines', line=dict(color='lightblue', width=2)
        ))
        fig.add_trace(go.Scatter3d(
            x=x[:, i, 0], y=x[:, i, 1], z=z[:, i], 
            mode='lines', line=dict(color='lightblue', width=2)
        ))


    def add_arrow(x_start, y_start, z_start, u, v, w, color, label=None):
        x_end = x_start + u
        y_end = y_start + v
        z_end = z_start + w
        # Draw the line (arrow shaft)
        fig.add_trace(go.Scatter3d(
            x=[x_start, x_end], y=[y_start, y_end], z=[z_start, z_end],
            mode='lines', line=dict(color=color, width=4)
        ))
        # Add an arrowhead (small point at the end)
        # fig.add_trace(go.Scatter3d(
        #     x=[x_end], y=[y_end], z=[z_end],
        #     mode='markers', marker=dict(symbol="arrow-bar-up", size=10, color=color)
        # ))
        # Add label if provided
        if label:
            fig.add_trace(go.Scatter3d(
                x=[x_end], y=[y_end], z=[z_end],
                mode='text', text=[label], textposition='top center', textfont=dict(color=color)
            ))

    # Add arrows to the plot
    add_arrow(0, 0, 0, q1[0], q1[1], 0, 'blue', label=r'q1')
    add_arrow(0, 0, 0, q2[0], q2[1], 0, 'green', label=r'q2')
    add_arrow(0, 0, 0, p1[0], p1[1], 0, 'red', label='grad p1' if swap else 'grad p2')

    fig.add_trace(go.Scatter3d(
            x=[q[0], q[0]], y=[q[1], q[1]], z=[0, 1],
            mode='lines+markers', line=dict(color='black', width=4, dash='longdash'),
            marker=dict(size=5, color='black')
        ))


    # Add axis lines
    fig.add_trace(go.Scatter3d(x=[-1.5, 1.5], y=[0, 0], z=[0, 0], mode='lines', line=dict(color='black', width=2)))  # X-axis
    fig.add_trace(go.Scatter3d(x=[0, 0], y=[-1.5, 1.5], z=[0, 0], mode='lines', line=dict(color='black', width=2)))  # Y-axis
    fig.add_trace(go.Scatter3d(x=[0, 0], y=[0, 0], z=[0, 1], mode='lines', line=dict(color='blue', width=1)))         # Z-axis
    fig.add_trace(go.Scatter3d(
        x=[0], y=[0], z=[1],
        mode='text', text=['1' if swap else 'i'], textposition='top center', textfont=dict(color='blue')
    ))

    # Set axis properties


    return fig

q1_better = np.array([1, .8]) /1.2
q2_better = np.array([-2, 1])/2.2
fig1 = create_3d_plot(q1_better,q2_better, True)
fig2 = create_3d_plot(q1_better,q2_better, False)

# Create a subplot figure
from plotly.subplots import make_subplots

fig = make_subplots(rows=1, cols=2, specs=[[{'type': 'surface'}, {'type': 'surface'}]])

# Add the first plot to the first subplot
for trace in fig1.data:
    fig.add_trace(trace, row=1, col=1)

# Add the second plot to the second subplot
for trace in fig2.data:
    fig.add_trace(trace, row=1, col=2)


# Update layout for the subplot figure
zoom = 1/1.7
scene = dict(
        xaxis=dict(range=[-2, 2], visible=False),
        yaxis=dict(range=[-2, 2], visible=False),
        zaxis=dict(range=[-1, 1.2], visible=False),
                    camera=dict(
                eye=dict(x=.4*zoom, y=-2*zoom, z=1.2*zoom),  # Camera positioned at (1.5, 1.5, 1.5)
                up=dict(x=0, y=0, z=1),         # Camera's "up" vector is along the Z-axis
                center=dict(x=0, y=0, z=0),      # Camera is looking at the origin
                                    # Zoom in by a factor of 2
            ),)
fig.update_layout(
    scene=scene,
    scene2=scene,
    showlegend=False
)


fig.show()

Or simply imagine mapping \(q_1\) to \(1\) and \(q_2\) to \(i\)

Code
# Create the figure and axis
fig, ax = plt.subplots(figsize=(4, 4))

# Set the axis limits
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)

# Add grid and axis labels
ax.axhline(0, color='black',linewidth=0.5)
ax.axvline(0, color='black',linewidth=0.5)
ax.set_xticks(np.arange(-2, 3, 1))
ax.set_yticks(np.arange(-2, 3, 1))
ax.grid(False)

# Define canonical vectors
e1 = np.array([1, 0])
e2 = np.array([0, 1])

# Plot the vectors
ax.quiver(0, 0, *e1, angles='xy', scale_units='xy', scale=1, color='blue', label=r'$pq_1$')
ax.quiver(0, 0, *e2, angles='xy', scale_units='xy', scale=1, color='green', label=r'$pq_2$')

# Add axis labels
ax.text(*e1, r'$pq_1$', fontsize=12, color='blue', verticalalignment='bottom', horizontalalignment='left')
ax.text(*e2, r'$pq_2$', fontsize=12, color='green', verticalalignment='top', horizontalalignment='right')

# Hide the grid and axes
ax.set_axis_off()

# Show the plot
plt.show()

Lets see how p(.) comutes with the linear map A.

Let \(A^*\) be the dual operator to \(A\). By definition

\[ p(Aq)=ipq \] \[ p(A\bar{q})=-ip\bar{q}=0=ip\bar{q} \]

So on the invariant subspace \(W\)

\[ Aw = A (aq_1 + bq_2) = aq_2 - bq_1 \]

\[ pAw = -b + ai = i(a + bi) = ipw \]

\[ \begin{array}{ccc} W & \xrightarrow{A} & W \\ \downarrow{p} & & \downarrow{p} \\ \mathbb{C} & \xrightarrow{i} & \mathbb{C} \end{array} \]

7

Code
# Create the figure and axis
fig, ax = plt.subplots(figsize=(4, 4))

# Set the axis limits
ax.set_xlim(-1, 1)
ax.set_ylim(-1, 1)

# Add grid and axis labels
ax.axhline(0, color='black',linewidth=0.5)
ax.axvline(0, color='black',linewidth=0.5)
ax.set_xticks(np.arange(-2, 3, 1))
ax.set_yticks(np.arange(-2, 3, 1))
ax.grid(False)

# Define canonical vectors
e1 = np.array([1, 0])
e2 = np.array([0, 1])

# Plot the vectors
ax.quiver(0, 0, *e2, angles='xy', scale_units='xy', scale=1, color='blue', label=r'$pAq_1 = ipq_1$')
ax.quiver(0, 0, *(-e1), angles='xy', scale_units='xy', scale=1, color='green', label=r'$pAq_2 = ipq_2$')

# Add axis labels
ax.text(*e2, r'$pAq_1 = ipq_1$', fontsize=12, color='blue', verticalalignment='bottom', horizontalalignment='left')
ax.text(*(-e1), r'$pAq_2 = ipq_2$', fontsize=12, color='green', verticalalignment='top', horizontalalignment='right')

# Hide the grid and axes
ax.set_axis_off()

# Show the plot
plt.show()

General case

First lets consider all \(\omega > 0\) (keep \(\sigma = 0\) ). In that case there is a simple connection with the previous discussion we can decompose the operator into two:

\[ A = \omega \frac{1}{\omega} A = \omega \bar{A} \] Where the operator

\[ \bar{A} = \frac{1}{\omega} A \]

satisfies the simpler (\(\omega=0\)) case and the multipliation by \(\omega\) is just a scaling operator. This trivially translates to scaling in the complex space. For \(\omega < 0\) this means scaling and flipping or slimply a rotation in the opposite direction ( \(Aq_1 = q_2,\; Aq_2=-q_1\) ).

In contrast permitting \(\sigma \neq 0\) adds hudge complexity - it can be viewed as rotation and then adding the original vector. The decomposition could look something like this:

\[ A = \omega \bar{A} + \sigma E \]

\[ Aq=\lambda q, \quad \lambda = \sigma + i\omega \]

\[ \bar{A}q=iq \]

Footnotes

  1. You can also imagine a general operator A on complex vector space \(V\) where \(AReV \subseteq ReV\)↩︎

  2. If \(\omega\) <0 the conjugated vector \(\bar{q}\) will have eigenvalue \(\bar{\lambda} = \sigma - i\omega\) ↩︎

  3. The upper index is related to the Einstains notaion (contravariet components). I think it is good to use the notation from fields that provide important backgroud, so those who know it can figure out the meaning faster and those who don’t are motivated to learn it.↩︎

  4. Throughout the following text you can just imagine a vector from \(\mathbb{R}^n\) as column, the dual vector as row and understand the expression \(p^1q_1\) as multiplication of \(1\times n\) and \(n \times 1\) matrices. If we denote the matrix formed from the base \(Q = (q_1,q_2,\dots)\). The i-th dual base vector is then obtained as a solution to the system \(p^iQ=e_i^T\). In other words the matrix of dual basis (composed of rows) is \(Q^{-1}\)↩︎

  5. This is some kind of dimensional reduction where we extract the extraordinary behavior on specific subspace.↩︎

  6. sadly without good reason why↩︎

  7. This can be also viewed as the dual operator \(A^* : W^* \rightarrow W^*\) Is just the multiplication by \(i\): \(A^*p = pA = ip\).↩︎