Working with Anymatrix

Anymatrix provides programmatic generation of test matrices with well-defined mathematical properties, inspired by the MATLAB anymatrix toolbox.

Overview

The anymatrix module includes 65+ test matrices organized into 5 groups:

  • core (45+ matrices): Miscellaneous test matrices

  • gallery (6 matrices): Classic MATLAB gallery matrices

  • hadamard (2 matrices): Hadamard and Walsh matrices

  • matlab (8 matrices): Standard MATLAB test matrices

  • regtools (5 matrices): Regularization problem matrices

Getting Started

Initialize Anymatrix

from matrix_toolkit.anymatrix import AnyMatrix

am = AnyMatrix()

Explore Available Matrices

List all groups:

groups = am.groups()
print(groups)
# ['core', 'gallery', 'hadamard', 'matlab', 'regtools']

List matrices in a group:

core_matrices = am.list('core')
print(f"Core group has {len(core_matrices)} matrices")
print(core_matrices[:10])

Get matrix count:

counts = am.count()
print(counts)
# {'core': 45, 'gallery': 6, 'hadamard': 2, 'matlab': 8,
#  'regtools': 5, 'total': 66}

Generating Matrices

Basic Generation

Generate a matrix by its ID (format: ‘group/name’):

# Beta matrix (symmetric positive definite)
beta = am.generate('core/beta', 10)
print(f"Shape: {beta.shape}")

# Fourier matrix (unitary)
fourier = am.generate('core/fourier', 8)

# Hilbert matrix (ill-conditioned)
hilbert = am.generate('matlab/hilbert', 5)

Shorthand Syntax

You can also use the callable syntax:

# These are equivalent
A = am.generate('core/beta', 10)
A = am('core/beta', 10)

Matrix with Parameters

Some matrices accept additional parameters:

# KMS matrix with different rho values
kms1 = am.generate('gallery/kms', 10, rho=0.5)
kms2 = am.generate('gallery/kms', 10, rho=0.9)

# Indefinite matrix with k negative eigenvalues
indef = am.generate('core/indef', 20, k=5)

# Wilkinson matrix (W+ or W-)
w_plus = am.generate('matlab/wilkinson', 21, mode='plus')
w_minus = am.generate('matlab/wilkinson', 21, mode='minus')

Matrix Properties

Viewing Properties

Get properties of a specific matrix:

props = am.properties('core/beta')
print(props)
# ['symmetric', 'positive definite']

Get properties of all matrices:

all_props = am.properties()
for matrix_id, props in list(all_props.items())[:5]:
    print(f"{matrix_id}: {props}")

Verifying Properties

from matrix_toolkit.anymatrix import MatrixProperties

# Generate a matrix
matrix = am.generate('core/beta', 10)

# Check individual properties
is_sym = MatrixProperties.is_symmetric(matrix)
is_pd = MatrixProperties.is_positive_definite(matrix)

print(f"Symmetric: {is_sym}")
print(f"Positive Definite: {is_pd}")

Check multiple properties at once:

expected_props = am.properties('core/beta')
results = MatrixProperties.check_properties(matrix, expected_props)

for prop, passed in results.items():
    status = "✓" if passed else "✗"
    print(f"{status} {prop}")

Searching Matrices

Search by Properties

Find matrices with specific properties:

# Find all symmetric matrices
symmetric = am.search(['symmetric'])
print(f"Found {len(symmetric)} symmetric matrices")

# Find symmetric AND positive definite
sym_pd = am.search(['symmetric', 'positive definite'])
print(sym_pd[:10])

Match Any vs. All

By default, search requires ALL properties to match:

# Must have BOTH properties
result = am.search(['symmetric', 'positive definite'], match_all=True)

To match ANY property:

# Can have EITHER property
result = am.search(['symmetric', 'orthogonal'], match_all=False)

Getting Help

Matrix Documentation

Get detailed information about a matrix:

help_text = am.help('core/fourier')
print(help_text)

Output:

Matrix: core/fourier
Group: core
Name: fourier

Description:
Discrete Fourier transform matrix

Properties:
  - unitary
  - symmetric

Matrix Groups Reference

Core Group

The core group contains miscellaneous test matrices:

Symmetric Positive Definite

  • beta - Beta matrix

  • wilson - Wilson matrix

Nilpotent

  • nilpot_triang - Upper triangular nilpotent

  • nilpot_tridiag - Tridiagonal nilpotent (Jordan block)

  • creation - Creation operator (quantum mechanics)

Stochastic

  • stoch_cesaro - Cesaro matrix (row stochastic)

  • stoch_compan - Stochastic companion matrix

  • stoch_perfect - Doubly stochastic shuffle

  • stoch_revtri - Upper triangular stochastic

  • symmstoch - Symmetric doubly stochastic

Structured

  • circul_binom - Circulant with binomial coefficients

  • blockcirc - Block circulant

  • perfect_shuffle - Perfect shuffle permutation

  • vecperm - Vec-permutation matrix

  • collatz - Collatz conjecture matrix

Orthogonal

  • fourier - Discrete Fourier transform

  • blockhouse - Block Householder

  • hess_orth - Orthogonal Hessenberg

  • orthog_cauchy - From Cauchy matrix

  • uptraporth - Upper trapezoidal orthogonal

  • unitary_eye - Unitary with unit eigenvalues

Special Constructions

  • gfpp - Large growth factor for Gaussian elimination

  • indef - Symmetric indefinite

  • totally_nonneg - Totally nonnegative

  • rschur - Random Schur decomposition

  • soules - With prescribed eigenvalues

Hadamard Group

Hadamard matrices and variants:

  • hadamard - Standard Hadamard matrix (order must be power of 2)

  • walsh - Walsh-Hadamard matrix (sequency ordered)

Example:

# Generate Hadamard matrix
H = am.generate('hadamard/hadamard', 16)

# Rows are orthogonal (after normalization)
assert MatrixProperties.is_orthogonal(H / np.sqrt(16))

MATLAB Group

Standard MATLAB test matrices:

  • magic - Magic square (rows, columns, diagonals sum to same value)

  • pascal - Pascal matrix (binomial coefficients)

  • rosser - Rosser eigenvalue test matrix

  • wilkinson - Wilkinson W+ or W- matrices

  • hilbert - Hilbert matrix (notoriously ill-conditioned)

  • invhilb - Exact inverse of Hilbert matrix

  • frank - Frank matrix (det = 1)

  • companion - Companion matrix of a polynomial

Example:

# Magic square
M = am.generate('matlab/magic', 5)

# All rows, columns, and diagonals sum to same value
magic_sum = M.sum(axis=0)[0]
assert np.allclose(M.sum(axis=0), magic_sum)
assert np.allclose(M.sum(axis=1), magic_sum)

# Hilbert and its inverse
H = am.generate('matlab/hilbert', 5)
InvH = am.generate('matlab/invhilb', 5)

# Verify they're inverses
assert np.allclose(H @ InvH, np.eye(5))

Regtools Group

Matrices from regularization problems (discrete ill-posed problems):

  • deriv2 - Second derivative operator (discrete Laplacian)

  • shaw - Shaw 1D image restoration problem

  • phillips - Phillips image deblurring problem

  • blur - Gaussian blur matrix for deconvolution

  • gravity - Gravity surveying problem

These return tuples of (A, b, x_true) for test problems:

# Shaw problem
A, b, x_true = am.generate('regtools/shaw', 50)

# Solve with regularization
from scipy.linalg import lstsq
x_lstsq = lstsq(A, b)[0]

# Compare to true solution
error = np.linalg.norm(x_lstsq - x_true)
print(f"Reconstruction error: {error:.2e}")

Advanced Usage

Custom Tolerance

Adjust tolerance for property checking:

# Stricter tolerance
is_sym = MatrixProperties.is_symmetric(matrix, tol=1e-12)

# More lenient
is_ortho = MatrixProperties.is_orthogonal(Q, tol=1e-6)

Batch Generation

Generate multiple matrices efficiently:

matrices = {}
for size in [10, 20, 50, 100]:
    matrices[size] = am.generate('core/beta', size)

# Verify properties for all
for size, matrix in matrices.items():
    props = am.properties('core/beta')
    results = MatrixProperties.check_properties(matrix, props)
    print(f"Size {size}: {all(results.values())}")

Working with Sparse Matrices

Some matrices return sparse formats:

# These are sparse by default
sparse_matrices = [
    'core/perfect_shuffle',
    'core/collatz',
    'core/vecperm',
    'regtools/deriv2',
]

for matrix_id in sparse_matrices:
    mat = am.generate(matrix_id, 100)
    if hasattr(mat, 'nnz'):
        density = mat.nnz / (mat.shape[0] * mat.shape[1])
        print(f"{matrix_id}: density = {density:.3%}")

Integration with SciPy

Anymatrix matrices work seamlessly with SciPy:

import scipy.linalg as la
import scipy.sparse.linalg as spla

# Dense matrix operations
A = am.generate('core/beta', 100)
eigenvalues = la.eigvals(A)

# Sparse matrix operations
L = am.generate('regtools/deriv2', 1000)
eigenvalues_sparse = spla.eigs(L, k=10, which='SM')

Best Practices

  1. Choose Appropriate Size: Start with small sizes for testing

  2. Verify Properties: Always check that generated matrices have expected properties

  3. Use Correct Group/Name: Use am.list() to find exact matrix names

  4. Check Documentation: Use am.help() to understand matrix parameters

  5. Handle Sparse Matrices: Be aware which matrices return sparse format

  6. Set Random Seeds: For reproducible random matrices

See Also