-
Notifications
You must be signed in to change notification settings - Fork 116
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
incomplete implementation of qr decomposition #371
Conversation
@JamesTev This doesn't work correctly. I am pretty sure it is a trivial sign error somewhere, but I don't find it at the moment. Do you think you could take a look at the code? micropython-ulab/code/numpy/linalg/linalg.c Lines 378 to 464 in cba8873
from ulab import numpy as np
a = np.array([[1, 2], [3, 4]])
q, r = np.linalg.qr(a)
print(q)
print(r) results in array([[0.3162277660168379, 0.9486832980505138],
[-0.9486832980505138, 0.3162277660168379]], dtype=float64)
array([[3.162277660168379, 4.427188724235731],
[-1.110223024625157e-16, -0.632455532033676]], dtype=float64) while (array([[-0.31622777, -0.9486833 ],
[-0.9486833 , 0.31622777]]),
array([[-3.16227766, -4.42718872],
[ 0. , -0.63245553]])) |
fix small glitch in optimize
Co-authored-by: Dan Halbert <[email protected]>
Co-authored-by: Dan Halbert <[email protected]>
add circuitpython compilation section to README
Lowercase MP macros
update ESP32 compile instructions in readme
Adding solve_triangular function in numpy.linalg module
change to lowercase macro style, so that `circuitpython` can automatically build
add docs for solve_triangular
update docs
@v923z Sorry for the late reply - just been swamped with uni work and exams. I will be sure to check this out soon once my exams are done! Thanks a lot. |
@v923z Has this issue been fixed or are you still working on the weird sign issue? I pulled this branch and flashed the new firmware and am getting some weird sign differences compared to normal numpy. I tested with
uLab gives:
So, there are sign issues in both Q and R matrices and Q matrix seems to be transposed in the uLab version. Did you find similar? |
Thanks for taking the time! I'll look into this. |
@JamesTev >>> from ulab import numpy as np
>>> A = np.arange(9).reshape((3,3))
>>> q, r = np.linalg.qr(A)
>>> print(q)
>>> print()
>>> print(r)
>>> print(np.dot(q.transpose(), r))
array([[0.0, -0.447213595499958, -0.8944271909999159],
[-0.9128709291752768, -0.3651483716701107, 0.1825741858350553],
[0.408248290463863, -0.8164965809277261, 0.408248290463863]], dtype=float64)
array([[-6.708203932499369, -8.049844718999243, -9.391485505499116],
[0.0, -1.095445115010332, -2.190890230020665],
[0.0, 0.0, -4.440892098500626e-16]], dtype=float64)
array([[0.0, 1.0, 2.0],
[3.0, 4.0, 4.999999999999999],
[6.0, 6.999999999999999, 7.999999999999998]], dtype=float64)
By the way, you can test things in the |
@JamesTev I have fixed the issue with the transpose. |
@v923z great, thank you! |
@JamesTev If you think it is OK, I would merge it. |
@JamesTev Do you think you could write a small test script? |
I verified that it works with the eigen solver algorithm that I’m using which is a good start. But yes, I can try think of some edge cases and get back to you. |
@JamesTev I have clean forgotten, I still have to implement the |
I noticed something a bit strange: as part of another calculation, I computed a matrix A as below:
The numpy and ulab QR functions gave different results. In particular, the ulab version gives an incorrect R matrix (not upper triangular). Do you think this could be due to some small numerical error that is very noticeable in this case because of the magnitude of the entries in A? I tried computing again with (A/1000) and the ulab-computed R matrix was much closer to the expected upper triangular form. This issue arises with different matrices (different A) too; it seems increasingly so as the magnitude of entries in A increases. Numpy result for
|
This seems to me to be a round-off error. With 64 bits, I get the following: >>> from ulab import numpy as np
>>> A = np.array([[-3.090257e+12, -7.008567e+05, 4.739371e+05, 2.532784e+05],
[-2.618467e+04, -3.090258e+12, 4.707228e+05, 2.515600e+05],
[-2.622055e+04, -6.970566e+05, -3.090256e+12, 2.519048e+05],
[-2.613640e+04, -6.948194e+05, 4.698551e+05, -3.090257e+12]])
>>> q, r = np.linalg.qr(A)
>>> print(q)
>>> print()
>>> print(r)
array([[-1.0, 8.473302314666956e-09, -8.484905986702145e-09, -8.457677823969375e-09],
[-8.473298499121594e-09, -1.0, -2.255657904819399e-07, -2.248419064713072e-07],
[-8.484909183928712e-09, -2.255658246297365e-07, 1.0, 1.520440346958196e-07],
[-8.457678439042448e-09, -2.248418721206846e-07, -1.520440853219895e-07, 1.0]], dtype=float64)
array([[3090257000000.001, 727041.3902643195, -447716.5664473673, -227142.0042689324],
[-1.04984705117826e-25, 3090258000000.152, 226333.2414550781, 443259.1143798828],
[2.568302040251067e-12, -0.0, -3090256000000.068, 721760.0402832033],
[-2.576570284484911e-12, 0.0, -0.000244140625, -3090256999999.907]], dtype=float64) I don't know what the proper solution would be. I believe, the culprit is this: micropython-ulab/code/numpy/linalg/linalg.c Lines 429 to 438 in a84db8f
You have values that differ by 8 orders of magnitude (e.g., in the first column, 3.090257e+12, and -2.618467e+04), and the Givens rotations actually compare these values, and I think that trips the condition in the second line of the snippet above. I guess, what happens is that compared to A[0,0], A[1,0] is zero, so for these values no actual rotation takes place. In other words, you just don't have enough depth to compare such wildly different values. https://en.wikipedia.org/wiki/Single-precision_floating-point_format I think, a fair comparison with |
@JamesTev Ha! This tripped me. I think you copied the wrong output.
I have also implemented the As for the sign errors that you found earlier, I think that is not a fundamental issue, and it shouldn't change too much. But if you want to, we could do a sweep before we return the matrices, and multiply the columns, if the diagonal entry is negative. I believe that should fix it. Next up is |
@v923z Sorry, I don't quite follow? I've repeated the experiment and checked the input matrix to both versions is the same and I get the same result (with both |
Great! Thanks a lot - I will have a look |
@JamesTev |
No problem! Any ideas on what the issue may be? Do you think it is indeed an issue with single precision? |
Yes, I am pretty confident. As far as I remember, on some platforms you can switch on double precision, even if the MCU doesn't have a proper FPU. Double precision will then be implemented in firmware. I don't know, whether your MCU falls in this category, and one also has to pay a speed penalty. |
I see. Forgive my ignorance but I'm using an ESP32 and believe it has a FPU. Do I need to set a flag somewhere in the ulab firmware and rebuild to enable double precision? |
No, that actually happens automatically here: micropython-ulab/code/ndarray.h Lines 30 to 34 in 764d5b9
So, you need to set the appropriate flag (MICROPY_FLOAT_IMPL) in |
@JamesTev James, I think this is what you need: micropython/micropython#4380 (comment) |
Yeah, found it. Thanks! I've just tested again with double precision enabled and QR is working as expected 👍 |
Thanks! If you can supply a small test script, I would like to merge this. |
Sure. Would a basic (micro)python script be okay? |
Yes. We can always extend it later, if needed. I just want something that, first, demonstrates the usage, second, provides a rudimentary safeguard against introducing error, if we change the code. |
Obsolete, fixed in #427. |
This PR partially implements the request in #363 by adding QR decomposition to
ulab
.