Arrows the Matrix Only Stretches

eigenvectors · eigenvalues · matrix-vector product · tolerance · complex numbers

A matrix here is a list of rows (m[i][j] is row i, column j), and a vector a list; entries are complex numbers (ints and floats are allowed too). The matrix-vector product is (Mv)_i = \sum_j M_{ij} v_j.

Most vectors change direction when a square matrix acts on them. A nonzero vector v that the matrix only scales,

M v = \lambda\, v \qquad\text{for some number } \lambda,

is an eigenvector of M, and \lambda (possibly complex, possibly 0) is its eigenvalue. The zero vector never counts as an eigenvector. Eigenvectors of gates will matter a lot later in the simulator you are building.

Write eigenvalue_of(m, v) that returns \lambda if v is an eigenvector of m, and None otherwise. Because of rounding, v counts as an eigenvector when some \lambda brings every entry within 1e-9: abs((M v)[i] - lam * v[i]) <= 1e-9 for every i. In every test, v either is an eigenvector up to rounding errors or no \lambda brings all entries within 0.001, so any reasonable way of handling the rounding gives the same answer.

Examples

Input:  m = [[0, 1], [1, 0]], v = [1, -1]
Output: -1
Explanation: M v = [-1, 1] = -1 * [1, -1].

Input:  m = [[0, -1], [1, 0]], v = [1, -1j]
Output: 1j
Explanation: M v = [0 * 1 + (-1) * (-1j), 1 * 1 + 0 * (-1j)] = [1j, 1] = 1j * [1, -1j].

Input:  m = [[0, 1], [1, 0]], v = [1, 0]
Output: None
Explanation: M v = [0, 1] points in a different direction.

Constraints

  • m is square and len(v) == len(m)
  • answers are compared with a tolerance of 1e-6

Goals

  • Check whether a vector is an eigenvector of a matrix and recover its eigenvalue
  • Pick a safe entry to divide by, never one that is 0
  • Tell the eigenvalue 0 apart from 'not an eigenvector'
Starting Python…