-
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
[FEATURE REQUEST] linalg.eig
for asymmetric matrices (and SVD?)
#363
Comments
That was the simplest case to implement. To be honest, you are the first, who has asked a question about The snag is, each type of matrix has its own numerical difficulties, and if you look at serious numerical packages, different methods are used for the different types. In other words, there is no silver bullet.
No, but this doesn't mean that it can't be.
Do you have a particular algorithm in mind, or would you like to take a stab at the implementation? |
I see - that makes sense.
I'm not too familiar with your particular
At the moment, I'm trying to compute CCA in real time on an ESP32 which requires SVD or being able to find eigen values. Unfortunately, my C skills aren't great so taking a stab at the implementation would take quite some time! As an aside: I think having fast numpy-like linear algebra functions is really useful and convenient and far easier to use than some of the standard C/C++ implementations. Please keep up the good work. |
I actually use Jacobi rotations, but that is off the point. I think QR would be relatively straightforward. Give me a couple of days. But that might not take you to the finish line. I forgot to mention yesterday that one of the reasons for sticking with symmetric matrices was that their eigenvalues are real, and I didn't want to deal with complex numbers. (I have nothing against them, but they add too much to the firmware.)
In that case, would an implementation of #188 help you? The only thing missing there is the addition of the
That's fine, but I might ask you to help with the testing.
This project lives by the interest of the users. |
@JamesTev I have opened a draft under #366. With that, we should be able to insert a generic eigenvalue solver in |
@v923z Awesome, thank you! I agree that it's important to focus resources on the most useful functionality. To this end, a generic eigenvalue solver would be useful for countless algorithms and applications. My need (to perform CCA for EEG analysis) is just one. Although dimensionality reduction type problems are often fine because covariance matrices are symmetric, I've encountered several problems that produce asymmetric ones. |
I'm going to chime in here saying that an SVD implementation would let me implement a generic pseudoinverse solver for elliptical fitting. I'm a PhD student at UW and currently my "SVD implementation" uses a rudimentary implementation based on power iteration, but a more robust and efficient approach such as one using the QR algorithm would be helpful. Seeing that the QR decomposition was implemented by #427, I don't believe it would be too bad to implement it now. Unfortunately numerical algorithms just aren't my area and I'm mainly interested in the applications. If it would be possible to implement generic SVD, that would be great. I can certainly try to help if need be with the SVD algorithm if time allows. |
There are a couple of features in the pipeline, but I might be able to work on this in the next one or two weeks. When this issue came up, I looked into possible implementations of the SVD algorithm, and it's definitely doable with moderate effort. |
@v923z - I'd like to support you to get SVD support added for ulab along with everything necessary to implement a homography. E.g. https://medium.com/analytics-vidhya/using-homography-for-pose-estimation-in-opencv-a7215f260fdd While I can easily implement a method to do this in OpenMV thanks to AprilTag's matrix library in our code base I think it should be in ulab instead of image library directly. Please let me know how I can help. ... All the matrix math you need: https://github.com/openmv/apriltag/blob/master/common/matd.c#L980 And how to do a homography: https://github.com/openmv/apriltag/blob/master/common/homography.c ... We use the homography_to_pose method to produce what we need given the camera matrix. |
@kwagyeman SVD in As for the homography routine, do you want to add that Also, would you be interested in supporting blob searching and similar? There is some code for that e.g., here: https://github.com/teuler/make-thermal-cam/blob/main/blob_ulab2_c_code/user.c, and I've been flirting with the idea for a long time. Having some image processing functionality could be useful beyond openmv, and I would be happy to add those functions, but I just don't know if there is interest. |
I'm wouldn't add anything to ulab that needs performance. Pretty much anything for image processing needs to go into SIMD land if you really want to push the speed. I'd keep it more high level and focused on doing mathematics for arrays. Note, The AprilTag folks released an AprilTag 3 library that may have an updated version of that library with faster/better code. As for the homography... I could add something to our image library to handle this. However, it may be more efficient to have in ulab since it needs the SVD code base. So, there would just be duplicates of the same code all over. Given that, adding the methods in their homography c file to a utils module that's generic for everyone to use would save code space I think in the long run. If you could make it possible to call these methods from C still that would be great. Then I could add a method to our image lib if we needed to for some reason without having two copies of the same base library. |
OK.
As far as I see, the last commit to the SVD routine was two years ago: https://github.com/AprilRobotics/apriltag/blob/master/common/svd22.c
One the one hand, I guess it would be enough, if we made the functions non-static, and then you can call them from everywhere. But on the other hand, if you want to implement another interface, that would definitely lead to code duplication, because the argument parsing etc. would be at two places. I might be missing the point, though. |
Argument parsing would be different with a different interface as it would specific for a different situation. Avoiding having two copies of the base library would be great. |
Reference documentation for |
@v923z I have two users asking about this. What would it take for this to get done quickly? |
I'll have it by the weekend, if that's not too late. *The SVD, that is. |
Look, I'm working on it: https://github.com/v923z/micropython-ulab/tree/svd! |
@v923z - That's awesome. Can I buy you a beer? |
I'm a teetotaller, so no. But thanks, anyway. However, what you could do is take a look at the code: I've adapted your implementation. At the end, there are three function calls to micropython-ulab/code/scipy/linalg/linalg.c Lines 761 to 766 in 5084bd4
It's not entirely clear to me what they should do, so you can either tell me what is to be implemented there, or you can insert the code. It seems to me that Also, there are commented-out calls to micropython-ulab/code/scipy/linalg/linalg.c Lines 687 to 695 in 5084bd4
|
Matd_op performs matrix math using the passed string: https://github.com/openmv/apriltag/blob/master/common/matd.h#L355 So, you can implement what that does by using your dot() and transpose() code. ... As for the code being implemented in line, that was probably for performance reasons. The comment is just showing what was there previously. |
I think the code is complete now, but it would need checking and testing. |
@v923z Awesome! Can you port the homography stuff too into a utils module? https://github.com/AprilRobotics/apriltag/blob/master/common/homography.h (Note that HOMOGRAPHY_COMPUTE_FLAG_INVERSE fails often. HOMOGRAPHY_COMPUTE_FLAG_SVD works more reliably).
Also, I'd love to provide some financial compensation for doing this port. It really helps all our users having access to this type of code. Having homography exposed in utils will allow folks to do awesome stuff. |
Yes, we could do that, but I actually wonder, whether we should just create a new module,
We can talk about this on a private channel. |
Many thanks! There are a couple of pointers here: https://github.com/v923z/micropython-ulab?tab=readme-ov-file#testing. You can take pretty much any of the test scripts as your boilerplate, e.g., https://github.com/v923z/micropython-ulab/blob/master/tests/2d/numpy/bitwise_or.py. Then you would just insert a matrix that is sort of meaningful, and try to verify that the results are correct. Once you're satisfied with the results, you can move your test script and the expected file to https://github.com/v923z/micropython-ulab/tree/master/tests/2d/scipy. |
Sorry for lack of updates: I'm a PhD student and am currently working on other projects. This currently isn't my priority. I might get to it after the next month when I'm more free. I think I can definitely write them though. |
@v923z I finally got around to running some preliminary tests. A couple things I noticed. You output a full diagonal matrix, which is unnecessary. The SciPy implementation of the SVD outputs the singular values as a 1D array. It works well enough with broadcasting and saves space, especially on a microcontroller. There is an option available in the SciPy implementation to output the full matrices using the parameter Another consideration is singular value ordering. SciPy orders them largest to smallest, and I believe this is to make low-rank approximation and PCA easier since it amounts to slicing off the first few entries. Either way though I haven't been able to get successful tests. This is my first time working on a collaborative project. I'm going to clean up my tests a bit, but how would you like me to make them available? Should I fork and PR? |
Yes, this is a good point, thanks for bringing it up! Here is an excerpt from the documentation:
It seems to me that the current implementation is equivalent to the default (True) value.
This is also true.
Yes, please! |
I did just did another check on this. This is actually not what this option does. The option switches between the full and reduced rank SVD. The reduced rank SVD does not compute columns of U and V that are associated with zero singular values. SciPy always outputs the singular values in a 1D array from largest to smallest. I'll continue the conversation there. |
Thanks for the clarification! |
@v923z I'm still willing to throw some money your way to get this feature working so we can have homography support in ulab. |
Hey sorry for the double misinformation, but I did another check! It's in fact not the reduced rank SVD! Apologies! I haven't ever touched that feature, but after some more reading I think it has to do with the Thin SVD. It's an optimization for m x n matrices that are especially tall or wide. I'm not too versed in this area, but according to Wikipedia, it says a step one QR decomposition is faster for tall/wide matrices which substantially speeds up the computation. I feel so silly getting it wrong twice. It's probably worth implementing as that's a large use case for SVD where one has a large amount of data points but a low number of dimensions of variation (as is often used for PCA). Not sure which LAPACK routine you're using, but SciPy defaults to |
Oh, don't worry, we all make mistakes! I've never used SVD, and I haven't experimented with the features in
For various reasons, we can't link against LAPACK, so the SVD is implemented from scratch. |
It's really not a question of money, but my ineptitude. I think the implementation (lifted from your code) is there, we've only got to iron out the details, and figure out how to make the return values |
Yeah, now that we have 4D tensors enabled on every single OpenMV Cam (except the M4 which is out of flash space - but, also super old) we will be leveraging and telling people to use ULAB a lot more. Expect usage of the library to go up significantly. |
@v923z - Still happy to pay for this feature to be worked on. |
We've almost solved the other problem, this is next ;-) |
I was going to comment that we could probably implement |
The tests are useful, as are your comments, thanks! I just have to sort out how we return the results. As for If I understand you correctly, you'd call |
I need to be able to find eigenvalues on an asymmetric square matrix (as offered by numpy), although it seems that
ulab.linalg.eig
only works on symmetric square matrices at the moment. Is there a reason for this? I assume it's for computational reasons.Also, is implementation of SVD and/or QR factorisation in the pipeline at all? I see you use Givens rotations for
linalg.eig
so QR shouldn't be too bad.Thanks for all your amazing work! This is an awesome project.
The text was updated successfully, but these errors were encountered: