Here's a rudimentary Python script to get a 2-D visualization.
import numpy as np
import matplotlib.pyplot as plt
from numpy import sin, cos, pi
A = np.array([ # matrix to visualize
[1, 2],
[3, 4]
])
angle = 30 * (pi/180) # ellipse rotation angle
axis_1 = 5 # length of axis 1
axis_2 = 3 # length of axis 2
th_range = np.linspace(0,2*pi,61)
# get points in a circle
x,y = cos(th_range), sin(th_range)
pts = np.vstack([x,y])
# stretch and rotate to form elipse
pts = pts * np.array([[axis_1],[axis_2]])
rot = np.array([
[cos(angle), -sin(angle)],
[sin(angle), cos(angle)]
])
pts = rot @ pts
# get points after transformation is applied
pts_after = A @ pts
plt.plot(*pts)
plt.plot(*pts_after)
plt.legend(['Before Transformation', 'After Transformation'])
plt.show()
Sample result:

More sophisticated version:
def fix_aspect(height=5, margin=0.1):
ax = plt.gca()
fig = plt.gcf() # added this
ax.set_aspect(1)
box = ax.get_tightbbox(fig.canvas.get_renderer()) # added single argument
box_width, box_height = box.x1 - box.x0, box.y1 - box.y0
aspect = box_height / box_width
width = height / aspect
# fig = plt.gcf()
fig.set_figwidth(width)
fig.set_figheight(height)
fig.tight_layout(pad=margin)
def draw_trans(A, angle = 0, axis_1 = 1, axis_2 = 1):
th_range = np.linspace(0,2*pi,61)
# get points in a circle
x,y = cos(th_range), sin(th_range)
pts = np.vstack([x,y])
# stretch and rotate to form elipse
pts = pts * np.array([[axis_1],[axis_2]])
rot = np.array([
[cos(angle), -sin(angle)],
[sin(angle), cos(angle)]
])
pts = rot @ pts
# get points after transformation is applied
pts_after = A @ pts
# get arrows
arrow_1 = np.array([cos(angle),sin(angle)])*axis_1
arrow_2 = np.array([cos(angle + pi/2),sin(angle + pi/2)])*axis_2
arrow_3 = A@arrow_1
arrow_4 = A@arrow_2
# plots
plt.plot(*pts)
plt.plot(*pts_after)
plt.annotate("", xy=arrow_1, xytext=(0, 0), arrowprops=dict(
arrowstyle="-|>", linewidth = 2, linestyle = '-', color = 'blue'))
plt.annotate("", xy=arrow_2, xytext=(0, 0), arrowprops=dict(
arrowstyle="-|>", linewidth = 2, linestyle = ':', color = 'blue'))
plt.annotate("", xy=arrow_3, xytext=(0, 0), arrowprops=dict(
arrowstyle="-|>", linewidth = 2, linestyle = '-', color = 'green'))
plt.annotate("", xy=arrow_4, xytext=(0, 0), arrowprops=dict(
arrowstyle="-|>", linewidth = 2, linestyle = ':', color = 'green'))
plt.legend(['Before Transformation', 'After Transformation'])
fix_aspect()
plt.show()
A = np.array([ # matrix to visualize
[1, 2],
[3, 4]
])
draw_trans(A, axis_2 = 2)
Result:
