0
$\begingroup$

Matrices send ellipses to ellipses. Is there any online visualization tool where I can enter a matrix $M$ and see a plot of ellipse $e$ being sent to $Me$? This would be very helpful to visualize classes of matrices such as symmetric, skew symmetric, orthogonal, etc.

Ideally, the tool should show ellipses in 2D and ellipsoids in 3D.

It doesn't have to be a dedicated tool: If there's a way to do this with a more general tool (e.g. Wolfram Alpha), that will be helpful as well.

$\endgroup$
3
  • $\begingroup$ GeoGebra (download free in many formats from www.geogebra.org) is easy to use. One quick thing to do in 2D is type an ellipse in the input line e,g $x^2+\frac{y^2}{2}=1$. Click on the point tool then on the ellipse to create point A. You can drag the point around the ellipse. Input your matrix e.g T = {{1,2},{-1,3}} then input T*A. Right click this new point and put Trace on. Now as you move the point on the ellipse you will trace out the transformation. $\endgroup$ Commented Nov 26, 2023 at 19:12
  • $\begingroup$ Are there any programming languages that you're familiar with that might be useful here? $\endgroup$ Commented Nov 26, 2023 at 20:37
  • $\begingroup$ @BenGrossmann I'm familiar with general purpose programming languages like Python, but not with mathematical languages like Mathematica $\endgroup$ Commented Nov 26, 2023 at 20:45

2 Answers 2

2
$\begingroup$

Here's a 3D plotter, albeit with limited functionality. It only can use the unit sphere as its starting ellipsoid, and it shows what happens to the transformation's principal axes or to the $i,j,k$ axes (but not to a user-specified set).

def draw_principle_3d(A, mode = 'principal'):
    lincount = 20
    u, v = np.mgrid[0:2*pi:lincount*2j, 0:pi:lincount*1j] 
    # u, v = np.mgrid[0:pi:20j, 0:pi:10j]
    X = cos(u)*sin(v)
    Y = sin(u)*sin(v)
    Z = cos(v)

    pts = np.array([x_vals,y_vals,z_vals]).transpose()
    pts_new = np.dot(pts, A.T).transpose()

    X_new,Y_new,Z_new = pts_new

    U,s,VT = la.svd(A)

    fig,ax = plt.subplots(figsize = (10,10),subplot_kw = {"projection":"3d"})
    ax.plot_wireframe(X, Y, Z, alpha = .1, color = 'tab:blue')
    ax.plot_wireframe(X_new, Y_new, Z_new, alpha = .1, color = 'tab:orange')

    org = np.zeros(3)
    
    if mode == 'principal':
        arrows_before = VT
        arrows_after = (s*U).T
    else:
        arrows_before = np.eye(3)
        arrows_after = A.T
    
    
    for row in arrows_before:
        a = Arrow3D(*zip(org,row), mutation_scale=20, 
                    lw=2, arrowstyle="-|>", color='blue')
        ax.add_artist(a)

    for row in arrows_after:
        a = Arrow3D(*zip(org,row), mutation_scale=20, 
                    lw=2, arrowstyle="-|>", color='green')
        ax.add_artist(a)
    
    plt.show()

Running this in the default mode, as in the code below, produces the picture below.

A = 0.7 * np.diag([1,2,3])
Q1,_ = la.qr(np.random.randn(3,3)) # random rotation
Q2,_ = la.qr(np.random.randn(3,3)) # random rotation

A = Q1@A@Q2

draw_principle_3d(A)

Result:

enter image description here

Running this in the "ijk" mode, as in the code below, produces the picture below.

A = 0.7 * np.diag([1,2,3])
Q1,_ = la.qr(np.random.randn(3,3)) # random rotation
Q2,_ = la.qr(np.random.randn(3,3)) # random rotation

A = Q1@A@Q2

draw_principle_3d(A, mode = 'ijk')

enter image description here

$\endgroup$
1
$\begingroup$

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:

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:

Second result

$\endgroup$
7
  • $\begingroup$ Is this the kind of thing you're looking for? If so, I could probably get a similar 3D visualization going $\endgroup$ Commented Nov 26, 2023 at 22:48
  • $\begingroup$ This is amazing! Exactly what I'm looking for. Could you also add to show two vectors and their image (e.g. draw $i$ in solid blue, $j$ in dotted blue, and their transformations in corresponding green). $\endgroup$ Commented Nov 26, 2023 at 22:52
  • $\begingroup$ @SRobertJames Do you actually want $i$ and $j$, or do you want the major/minor axis directions of the starting ellipse? Did you just want the starting ellipse to be a unit circle? $\endgroup$ Commented Nov 26, 2023 at 22:53
  • $\begingroup$ Major and minor axes, and yes, default to a unit circle (but I'd like to be able to edit that). $\endgroup$ Commented Nov 26, 2023 at 22:54
  • $\begingroup$ See my latest edit $\endgroup$ Commented Nov 26, 2023 at 23:27

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.