Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Denormalize unit vector

Suppose I have the unit vector, u.

from numpy import mat
u = mat([[0.9**0.5], [-(0.1)**0.5]])
# [ 0.9486833  -0.31622777]

The unit vector is an eigenvector of a matrix with integer entries. I also know that the eigenvalues are integers. So, the unit vector will contain irrational decimals that, when squared, are decimal approximations of rational numbers.

Is there any good way to find the smallest value k such that all entries of ku are integers? In general, k will be the square root of an integer.

A naive approach would be to square each entry in the vector and find the smallest ki that produces an integer. Then, k will be the square root the of LCM of all ki. I'm hoping there is a better approach than this.


Note that this is not a duplicate of this question.

like image 347
Jared Goguen Avatar asked Mar 30 '16 21:03

Jared Goguen


1 Answers

I've improved the methodology provided by Christian in order to accept a wider range of fractions. The trick was to "pre-normalize" the unit vector by dividing it by the smallest non-zero entry. This works reliably for all fractions of a specified maximum denominator.

from fractions import Fraction, gcd

def recover_integer_vector(u, denom=50):
    '''
    For a given vector u, return the smallest vector with all integer entries.
    '''

    # make smallest non-zero entry 1
    u /= min(abs(x) for x in u if x)

    # get the denominators of the fractions
    denoms = (Fraction(x).limit_denominator(denom).denominator for x in u)

    # multiply the scaled u by LCM(denominators)
    lcm = lambda a, b: a * b / gcd(a, b)
    return u * reduce(lcm, denoms)

Testing:

The following code was used to ensure that all fractions in the given range work correctly.

import numpy as np

from numpy import array
from itertools import combinations_with_replacement


for t in combinations_with_replacement(range(1, 50), 3):
    if reduce(gcd, t) != 1: continue

    v = array(map(float, t))
    u = v / np.linalg.norm(v)

    w = recover_integer_vector(u)

    assert np.allclose(w, v) or np.allclose(w, -v)

As can be seen by the testing time, this code is not very quick. It took my computer about 6 seconds to test. I'm not sure whether the timing can be improved.

like image 190
Jared Goguen Avatar answered Oct 11 '22 12:10

Jared Goguen