Jupyter+ipwidgets GUI for Nilpotents to Permutation Conjugacy Class Correspondence

Jupyter+ipwidgets GUI for Nilpotents to Permutation Conjugacy Class Correspondence

I was looking for a family of symplectic nilpotent matrices with prescribed associated permutation conjugacy classes. So I made a GUI in Jupyter+ipywidgets to go over matrices, and it actually worked.

Here is the code the GUI:

In [1]:
from collections import Counter
import numpy as np
import ipywidgets as widgets
In [2]:
def get_k(A):
    """Get nullity sequence of A"""
    n = len(A)
    k = []
    for i in range(2 * n + 1):
        nullity = 2*n - np.linalg.matrix_rank(np.linalg.matrix_power(A, i))
        k.append(nullity)
        if nullity == 2 * n:
            break
    else:
        return None
    return np.array(list(filter(bool, np.diff(k))))

def k_to_perm(k):
    """Convert nullity sequence to permutation conjugacy class by taking dual and fancy python"""
    if k is None:
        return None
    perm = Counter()
    for i in range(1, max(k) + 1):
        perm += Counter({np.sum(k >= i)})
    return perm

def pretty_perm(p):
    """Return nice two-rowed representation of permutaion conjugacy class"""
    if p is None:
        return 'not nilpotent'
    top = ''
    bottom = ''
    for i in sorted(p.keys()):
        b = str(i)
        t = str(p[i])
        bottom += b + '  '
        top += (' ' * len(b)) + t + ' '
    return '%s\n%s' % (top, bottom)

def favorite_symplectic(n):
    w = np.zeros((n, n), np.int64)
    for i in range(n//2):
        w[i, n - i - 1] = 1
    for i in range(n//2, n):
        w[i,  n - i - 1] = -1
    return w

def favorite_orthogonal(n):
    w = np.zeros((n, n), np.int64)
    for i in range(n):
        w[i, n - i - 1] = 1
    return w

def show_grid(n, preserve=None):
    """Show nxn grid of buttons and a textarea for permutation conjugacy class"""
    if preserve is not None:
        if preserve == 'symplectic':
            preserve = favorite_symplectic(n)
        elif preserve == 'orthogonal':
            preserve = favorite_orthogonal(n)
        w_inv = np.linalg.inv(preserve)
        
    A = np.zeros((n, n), np.int64)
    bs = []
    txt = widgets.Textarea()
    
    for i in range(n):
        row = []
        for j in range(n):
            w = widgets.Button(description='0', layout=widgets.Layout(width='5px'))
            w.style.button_color = 'red'
            def foo(b, i=i, j=j):
                if b.description == '0':
                    v, c = 1, 'lightgreen'
                else:
                    v, c = 0, 'red'
                    
                A[i, j] = v
                b.description = str(v)
                b.style.button_color = c
                
                if preserve is not None:
                    M = np.zeros((n, n), np.int64)
                    M[i, j] = 1
                    M = -np.dot(np.dot(w_inv, M.T), preserve)
                    (i2,), (j2,) = np.where(M)
                    if (i, j) != (i2, j2):
                        A[i2, j2] = int(M[i2, j2] * v)
                        bs[i2][j2].description = str(A[i2, j2])
                        bs[i2][j2].style.button_color = c
                    
                txt.value = pretty_perm(k_to_perm(get_k(A)))
            w.on_click(foo)
            row.append(w)
        bs.append(row)

    display(widgets.VBox([widgets.HBox(row) for row in bs] + [txt]))
In [3]:
show_grid(4, 'orthogonal')

Leave a comment