Skip to content
This repository has been archived by the owner on Jul 1, 2023. It is now read-only.

[Linear Algebra] Add eye and svd functions #578

Merged
merged 19 commits into from
Jan 9, 2020
Merged
58 changes: 58 additions & 0 deletions Sources/TensorFlow/Operators/LinearAlgebra.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,17 @@ public extension Tensor where Scalar: TensorFlowNumeric {
_Raw.matrixDiag(diagonal: self)
}

/// Returns a batched matrix tensor with new batched diagonal values.
/// Given the input tensor and diagonal, this operation returns a tensor with the same
/// shape and values as the input, except for the specified diagonals of the innermost matrices
/// which will be overwritten by the values in diagonal.
///
/// Parameter diagonal: A tensor with rank k.
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
@inlinable
func setDiagonal(diagonal: Tensor<Scalar>) -> Tensor {
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
_Raw.matrixSetDiag(self, diagonal: diagonal)
}

/// Returns a copy of a innermost tensor defined by a central band boundaries.
/// The output is a tensor of the same shape as the instance `[..., :, :]`.
///
Expand Down Expand Up @@ -106,6 +117,31 @@ internal extension Tensor where Scalar: TensorFlowFloatingPoint {
}
}

/// Returns an identity matrix or a batch of matrices.
///
/// - Parameters:
/// - numRows: A non-negative integer giving the number of rows in each batch matrix.
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
/// - numCols: A non-negative integer giving the number of rows in each batch matrix.
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
/// - batchShape: A list of integers. The returned tensor will have leading batch dimensions
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
/// of this shape.
public func eye<Scalar: Numeric>(
numRows: Int,
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
numCols: Int,
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
batchShape: [Int]
) -> Tensor<Scalar> {
let diagonalSize = min(numRows, numCols)
let diagShape = batchShape + [diagonalSize]
let diagonalOnes = Tensor<Scalar>(ones: TensorShape(diagShape))
if numRows == numCols{
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
return diagonalOnes.diagonal()
}
else{
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
let shape = batchShape + [numRows, numCols]
let zeroMatrix = Tensor<Scalar>(zeros: TensorShape(shape))
return zeroMatrix.setDiagonal(diagonal: diagonalOnes)
}
}

// MARK: - Decompositions

/// Returns the Cholesky decomposition of one or more square matrices.
Expand Down Expand Up @@ -148,4 +184,26 @@ public extension Tensor where Scalar: TensorFlowFloatingPoint {
func qrDecomposition(fullMatrices: Bool = false) -> (q: Tensor<Scalar>, r: Tensor<Scalar>) {
_Raw.qr(self, fullMatrices: fullMatrices)
}

/// Returns the singular value decompositions of one or more matrices.
///
/// Computes the SVD of each inner matrix in the `input` tensor such that
/// `input[..., :, :] = u[..., :, :] * diag(s[..., :, :]) * transpose(v[..., :, :])`
///
/// - Parameters:
/// - computeUv: If `true` then left and right singular vectors will be computed and
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
/// returned in `u` and `v`, respectively. Otherwise, only the singular values will be
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
/// computed, which can be significantly faster.
/// - fullMatrices: If `true`, compute full-sized `u` and `v`. If `false` compute only the
Shashi456 marked this conversation as resolved.
Show resolved Hide resolved
/// leading `min(shape[rank - 1], shape[rank - 2])` singular vectors. Ignored if
// `computeUv` is `false`.
@inlinable
func svd(computeUv: Bool = true, fullMatrices: Bool = false
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you rename computeUv to computeUV, uv or something more readable? It might follow the language style, but it is slightly obscure, what is meant by computeUv.

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 think I took this variable name from the raw ops generated. I do agree that it sounds sort of vague.

But would like @dan-zheng's idea on this

Copy link
Member

Choose a reason for hiding this comment

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

I think computeUV is the better name, since Uv is more easily misinterpreted as one camelcased entity than UV. Could you please update the parameter name and doc comments?

Copy link
Member

Choose a reason for hiding this comment

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

A semantic name like computeLeftAndRightSingularVectors may be more idiomatic than computeUV. I'm not sure what's the best semantic name, so using computeUV for now sounds good to me, to be consistent with numpy.svd and tf.linalg.svd.

) -> (s: Tensor<Scalar>, u: Tensor<Scalar>?, v: Tensor<Scalar>?) {
let (s, u, v) = _Raw.svd(self, computeUv: computeUv, fullMatrices: fullMatrices)
dan-zheng marked this conversation as resolved.
Show resolved Hide resolved
if !computeUv {
return (s, nil, nil)
}
return (s, u, v)
}
}
6 changes: 3 additions & 3 deletions Tests/TensorFlowTests/OperatorTests/LinearAlgebraTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@ final class LinearAlgebraTests: XCTestCase {
[0.02154995, 0.2738613]],
[[2.4748755, -0.7071073],
[-0.7071073, 0.3535535]]])
assertEqual(computedGradient, expectedGradient, accuracy: 1e-5)
assertEqual(computedGradient, expectedGradient, accuracy: 1e-5)
}

func testQRDecompositionApproximation() {
let shapes = [[5, 8], [3, 4, 4], [3, 3, 32, 64]]
for shape in shapes {
Expand All @@ -59,7 +59,7 @@ final class LinearAlgebraTests: XCTestCase {
assertEqual(aReconstitutedFull, a, accuracy: 1e-5)
}
}

static var allTests = [
("testCholesky", testCholesky),
("testQRDecompositionApproximation", testQRDecompositionApproximation)
Expand Down