Replace array_search with floating-point filter in Eigenvector::eigenvectors#473
Conversation
|
Hi @Aweptimum, Thank you for the separate PR to add this improvement. Before I merge it, I wanted to ask if you had any unit tests cases to add that would pass with the fix and fail without it? If so I'd love to add those as regression protection against future changes. Thanks, |
7c75688 to
c15924f
Compare
|
@markrogoyski I added a test case and rearranged the commit order so it's easier to go back and forth. I basically mimicked what happened in the QR Algorithm PR that caused failures. I put the matrices with duplicate eigenvalues in another dataprovider and added their eigenvalues into the data. In the added test, I added a really small numerical imprecision to them before passing them to the eigenvectors method. I think I may have made the offset a little too small because they don't always fail. Edit: yeah changing from 10-15 to 10-12 makes both fail |
To mimic the error found in the QR algorithm, we have to test with matrices that have duplicate eigenvalues and introduce numerical precision errors. To do this, a list of perturbed eigenvalues is passed to the eigenvectors method. The perturbation is achieved by adding a random +/- offset on an order of magnitude smaller than the default matrix error. This should allow the math to work out fine while causing the floating point comparison to fail.
array_search seems to fail in most cases when looking for a float in an array of floats. And even if it does find a match, if the key is 0, php evaluates `!0` to true. To find a match, we can instead loop through and compare the numbers with `Arithmetic::almostEqual` and then explicitly check if `$key === false`
c15924f to
163c0f9
Compare
Thanks. To be safe, due to the random element in the unit test, I ran them 1,000 times and no issues. Thanks. |
Addresses the error found in #472
When given eigenvalues containing duplicates, the
Eigenvector::eigenvectorsmethod fails in cases where the$eigenvaluesarray is made up of floats. The culprit is the floating point comparison inarray_searchwhen checking for orthogonal eigenvectors. Even in the case it does find a match, it can still fail if the match is at index 0 because the body of the method is guarded by a check for!$key. If$key = 0, php evaluates!0to true because 0 is falsey, so it can still fail to return the correct eigenvectors.We can instead loop through the found solutions, checking if the current eigenvalue has been solved with
Arithmetic::almostEqual, and then explicitly check if$key === false.I noticed that there aren't any test cases in the EigenvectorTest that would have caught this - should I add a few?