Linear system solution with fractions in numpy

Question:

I have matrix A and a right-hand-side vector y expressed in terms of fractions.Fraction objects:

import random, fractions, numpy as np

A = np.zeros((3, 3), dtype=fractions.Fraction)
y = np.zeros((3, 1), dtype=fractions.Fraction)
for i in range(3):
    for j in range(3):
        A[i, j] = fractions.Fraction(np.random.randint(0, 4), np.random.randint(1, 6))
    y[i] = fractions.Fraction(np.random.randint(0, 4), np.random.randint(1, 6))

I would like to solve the system A*x = y using the provided functions in numpy and get a result expressed in fraction objects, but unfortunately the basic x = np.linalg.solve(A, y) returns the result in standard floating point values:

>>> np.linalg.solve(A, y)

array([[-1.5245283 ],
       [ 2.36603774],
       [ 0.56352201]])

Is there a way of getting the exact result with fraction objects?


EDIT

What I would like to do is just not feasible with the built-in functionalities of numpy (as of version 1.10 – see Mad Physicist’s answer). What one could do is implementing his/her own linear solver based on Gauss elimination, which relies on sum, subtraction, multiplication and division, all of which are well-defined and executed exactly with fraction objects (as long as the numerators and denominators fit in the data type, which I think is arbitrarily long).

If you are really interested in having this, just implement a solver yourself, it will be easy and fast to do (follow one of the many tutorials online). I’m not that much interested, so I will stick to the floating point result.

Asked By: Spiros

||

Answers:

It does not appear to be possible to invert a matrix of rationals using pure numpy according to this thread on the python mailing list. The response suggests that you can use sympy for matrices of rationals up to size 4×4. If you are tied to numpy for some reason, you can consider taking and using the inverse of a 3×3 matrix “manually”. Step by step tutorials on how to do this can be found on http://www.mathsisfun.com/algebra/matrix-inverse-minors-cofactors-adjugate.html, as well as a large multitude of other tutorials on matrix inversion.

Answered By: Mad Physicist

IMHO, there is no hope. A solution that work in many cases :

y = np.zeros(3, dtype=fractions.Fraction)
....
X= np.linalg.solve(A,y)
s=[fractions.Fraction.from_float(x).limit_denominator(6**9) for x in X]
print(s,y==dot(A,s))

It uses the property that the solution is nearly a fraction with little numerator and denominator, and find it.

Answered By: B. M.

Even easier than Gauss elimination, you can implement your own solver based on Cramer’s rule.

import numpy as np
import fractions

def solve_in_rationals(A: np.ndarray, b: np.ndarray):
    d = int(np.rint(np.det(A)))
    sol = []
    for i in range(np.shape(A)[0]):
        Ai = np.copy(A)
        Ai[:, i] = b
        sol.append(fractions.Fraction(int(np.rint(np.det(Ai))), d))
    return sol
Answered By: Alehud