Skip to content
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

Matrix eigendecomposition #95

Merged
merged 10 commits into from
Feb 9, 2019

Conversation

loufranco
Copy link
Contributor

This PR tries to bring the almost-done eigendecomposition branch into Surge

I expect there to be some comments (it is not lint warning free right now)

  1. I got the older branch updated and fixed conflicts
  2. I changed the interface to support complex vector and matrix returns
  3. I added tests based on Intel LAPACK and numpy docs

But,

  1. My return is a 3-tuple, which generates a lint warning
  2. I am using (Double, Double) for complex numbers. I don't see a convention in Surge for this, but if there is one, let me know
  3. I can't use Matrix with that, so I use [[(Double, Double)]]

Ben Robbins and others added 8 commits April 20, 2015 23:16
Copy link
Collaborator

@alejandro-isaza alejandro-isaza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Just a few style comments.

// https://software.intel.com/sites/products/documentation/doclib/mkl_sa/11/mkl_lapack_examples/dgeev.htm
private func buildEigenVector(wi: [Double], v: [Double], result: inout [[(Double, Double)]]) {
let n = wi.count
for r in 0..<n {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not a fan of one-letter variable names, let use better names.

// .0: The eigenvalues represented as an array of complex numbers
// .1: The left eigenvectors represented as a 2-dimensional array of complex numbers
// .2: The right eigenvectors represented as a 2-dimensional array of complex numbers
public func eigendecompostion(_ x: Matrix<Float>) -> ([(Float, Float)], [[(Float, Float)]], [[(Float, Float)]]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use three slashes for doc comments /// also use swift doc comment style. See https://nshipster.com/swift-documentation/

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's define a struct for the return type.

// .0: The eigenvalues represented as an array of complex numbers
// .1: The left eigenvectors represented as a 2-dimensional array of complex numbers
// .2: The right eigenvectors represented as a 2-dimensional array of complex numbers
public func eigendecompostion(_ x: Matrix<Double>) -> ([(Double, Double)], [[(Double, Double)]], [[(Double, Double)]]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments as above.

// dgeev_ needs column-major matrices
var mat: [__CLPK_doublereal] = transpose(x).grid

var N1 = __CLPK_integer(x.rows)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

variable names should be lowercase, also let's use better names.

@loufranco
Copy link
Contributor Author

loufranco commented Feb 9, 2019

The build failure is because of strict lint issues with the 3-tuples I used

I'd like some guidance on the lint warning about using a tuple of 3 items. I could

  1. Suppress it
  2. Make a struct to hold the return
  3. Only return the right eigenvector (this is what numpy does), and use a 2-tuple -- this would make the function have a very analogous signature to numpy. I don't know how important having the left eigenvector is.

- better variable names
- use a struct for the decomposition result
- use standard Swift doc comments
@loufranco
Copy link
Contributor Author

@alejandro-isaza I implemented your suggestions.

Copy link
Collaborator

@alejandro-isaza alejandro-isaza left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just a couple more things.

/// - x: a square matrix
/// - Returns: a struct with the eigen values and left and right eigen vectors using (Double, Double)
/// to represent a complex number.
public func eigendecompostion(_ x: Matrix<Double>) -> MatrixEigenDecompositionResult<Double> {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot better. Let's make this function a verb. What about decompose? or eigenDecompose? or decomposeSpectrally? Also missing triple slash in the first three lines.

let decompositionJobV = UnsafeMutablePointer<Int8>(mutating: ("V" as NSString).utf8String)
// Call dgeev to find out how much workspace to allocate
dgeev_(decompositionJobV, decompositionJobV, &matrixRowCount, &matrixGrid, &eigenValueCount, &eigenValueRealParts, &eigenValueImaginaryParts, &leftEigenVectorWork, &leftEigenVectorCount, &rightEigenVectorWork, &rightEigenVectorCount, &workspaceQuery, &workspaceSize, &error)
assert(error == 0, "Matrix not eigen decomposable")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Asserts are bad here because they will be ignored in release build which will probably make the below code crash. Let's make a enum EigenDecompositionError and throw that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will fix -- I was trying to follow the conventions in the rest of the file (and Surge seems generally not to throw), but I agree that it's better.

- rename func to eigenDecompose
- fix some doc comments
- throw instead of asserting
- added a test for non-square matrices
@loufranco
Copy link
Contributor Author

@alejandro-isaza ok -- throw is in (with a test for non-square). As far as I can tell, there is no real matrix that isn't decomposable to complex results, but since DGEEV could return an error, I throw -- there is no test I could write to test it, though.

@alejandro-isaza alejandro-isaza merged commit a6dd5f6 into Jounce:master Feb 9, 2019
@alejandro-isaza
Copy link
Collaborator

Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants