Baunsgaard commented on a change in pull request #1489:
URL: https://github.com/apache/systemds/pull/1489#discussion_r775948322
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -33,6 +33,9 @@
import org.apache.commons.math3.linear.SingularValueDecomposition;
import org.apache.sysds.runtime.DMLRuntimeException;
import org.apache.sysds.runtime.data.DenseBlock;
+import org.apache.sysds.runtime.functionobjects.*;
Review comment:
don't do wildcard imports, only import things that are in use.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
Review comment:
this isSym check is taking much of your time,
In the computeEigen we have skipped this check, i think it is fair to do the
same here.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
Review comment:
use higher abstraction as the field type (ScalarOperator)
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
Review comment:
i think we have a in place transpose of vectors. make sure you use it
here to avoid allocation
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
+ double tol = 1e-10;
+ int m = in.rlen;
+
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0);
Review comment:
use other constructor
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
+ double tol = 1e-10;
+ int m = in.rlen;
+
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_prod.setValue(i, i, 1.0);
+ }
+
+ for(int i = 0; i < num_iterations; i++) {
+ MatrixBlock[] QR = computeQR2(in);
+ Q_prod = Q_prod.aggregateBinaryOperations(Q_prod,
QR[0], op_mul_agg);
+ in = in.aggregateBinaryOperations(QR[1], QR[0],
op_mul_agg);
+ }
+
+ for(int i = 0; i < m; i++) {
+ for(int j = 0; j < m; j++) {
+ if(i != j) {
+ if(Math.abs(in.getValue(i, j)) > tol)
+ throw new
DMLRuntimeException("QR Eigen Decomposition not converged or contains complex
EVs"
+ + " (position i = " + i
+ ", j = " + j + ")");
+ }
+ }
+ }
+
+ // If complex evals then A is in real Schur-form
+ // 2x2 blocks on diagonal of A correspond to a matrix that has
the same complex evals
+ double[] eval = new double[m];
+ for(int i = 0; i < m; i++) {
+ eval[i] = in.getValue(i, i);
+ }
+
+ double[][] evec =
DataConverter.convertToArray2DRowRealMatrix(Q_prod).getData();
+ return sortEVs(eval, evec);
+ }
+
+ /**
+ * Function to compute the Householder transformation of a Matrix.
+ *
+ * @param in Input Matrix
+ * @return transformed matrix
+ */
+ private static MatrixBlock computeHouseholder(MatrixBlock in) {
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ for(int k = 0; k < m - 2; k++) {
+ MatrixBlock ajk = A_n.slice(0, m - 1, k, k);
+ for(int i = 0; i <= k; i++) {
+ ajk.setValue(i, 0, 0.0);
+ }
+ double alpha = Math.sqrt(ajk.sumSq());
+ double ak1k = A_n.getDouble(k + 1, k);
+ if(ak1k > 0.0)
+ alpha *= -1;
+ double r = Math.sqrt(0.5 * (alpha * alpha - ak1k *
alpha));
+ MatrixBlock v = new MatrixBlock(m, 1, 0.0);
+ v.copy(ajk);
+ v.setValue(k + 1, 0, ak1k - alpha);
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 2 * r, num_Threads);
+ v = v.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock P = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ P.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_add =
InstructionUtils.parseExtendedBinaryOperator("+");
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+
+ MatrixBlock v_t = v.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ v_t = v_t.binaryOperations(op_add, v_t);
+ MatrixBlock v_v_t_2 = A_n.aggregateBinaryOperations(v,
v_t, op_mul_agg);
+ P = P.binaryOperations(op_sub, v_v_t_2);
+ A_n = A_n.aggregateBinaryOperations(P,
A_n.aggregateBinaryOperations(A_n, P, op_mul_agg), op_mul_agg);
+ }
+ return A_n;
+ }
+
+ /**
+ * Sort the eigen values (and vectors) in increasing order (to be
compatible w/ LAPACK.DSYEVR())
+ *
+ * @param eValues Eigenvalues
+ * @param eVectors Eigenvectors
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] sortEVs(double[] eValues, double[][]
eVectors) {
Review comment:
consider if you can modify this method to not use the double [][] of
eVectors, but instead use a the already materialized double[] underlying the
MatrixBlock.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
Review comment:
give the number of threads as argument to the method.
and add tests with 1 and more threads.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
Review comment:
this can be done in place on v1
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
Review comment:
you can allocate the matrix with a specific value in one of the other
constructors.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
Review comment:
i like the Coffee but allow the method to have a seed argument (you can
always add coffee to the seed if you want to have a little easteregg)!
i would like it if you isolate the entire allocation of v1 into another
method, this method should return the normalized v1.
This allows us to consider if we can find better ways of generating this,
and profilers will make it obvious if this is a bottleneck.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
+ double tol = 1e-10;
Review comment:
tolerance in argument
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
Review comment:
add threads argument
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
Review comment:
remove commented code
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
+ double tol = 1e-10;
+ int m = in.rlen;
+
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_prod.setValue(i, i, 1.0);
+ }
+
+ for(int i = 0; i < num_iterations; i++) {
Review comment:
add todo or make this loop parallel.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
+ double tol = 1e-10;
+ int m = in.rlen;
+
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_prod.setValue(i, i, 1.0);
+ }
+
+ for(int i = 0; i < num_iterations; i++) {
+ MatrixBlock[] QR = computeQR2(in);
+ Q_prod = Q_prod.aggregateBinaryOperations(Q_prod,
QR[0], op_mul_agg);
+ in = in.aggregateBinaryOperations(QR[1], QR[0],
op_mul_agg);
+ }
+
+ for(int i = 0; i < m; i++) {
+ for(int j = 0; j < m; j++) {
+ if(i != j) {
+ if(Math.abs(in.getValue(i, j)) > tol)
Review comment:
call getDenseBlockValues to allow you to iterate through a double[]
array directly instead.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
Review comment:
Use higher abstraction for the field type
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
+ double tol = 1e-10;
+ int m = in.rlen;
+
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_prod.setValue(i, i, 1.0);
+ }
+
+ for(int i = 0; i < num_iterations; i++) {
+ MatrixBlock[] QR = computeQR2(in);
+ Q_prod = Q_prod.aggregateBinaryOperations(Q_prod,
QR[0], op_mul_agg);
+ in = in.aggregateBinaryOperations(QR[1], QR[0],
op_mul_agg);
+ }
+
+ for(int i = 0; i < m; i++) {
+ for(int j = 0; j < m; j++) {
+ if(i != j) {
+ if(Math.abs(in.getValue(i, j)) > tol)
+ throw new
DMLRuntimeException("QR Eigen Decomposition not converged or contains complex
EVs"
+ + " (position i = " + i
+ ", j = " + j + ")");
+ }
+ }
+ }
+
+ // If complex evals then A is in real Schur-form
+ // 2x2 blocks on diagonal of A correspond to a matrix that has
the same complex evals
+ double[] eval = new double[m];
+ for(int i = 0; i < m; i++) {
+ eval[i] = in.getValue(i, i);
+ }
+
+ double[][] evec =
DataConverter.convertToArray2DRowRealMatrix(Q_prod).getData();
Review comment:
if we can avoid getting the values out as 2dRowRealMatrix, then it would
be nice.
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
+ double tol = 1e-10;
+ int m = in.rlen;
+
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_prod.setValue(i, i, 1.0);
+ }
+
+ for(int i = 0; i < num_iterations; i++) {
+ MatrixBlock[] QR = computeQR2(in);
+ Q_prod = Q_prod.aggregateBinaryOperations(Q_prod,
QR[0], op_mul_agg);
+ in = in.aggregateBinaryOperations(QR[1], QR[0],
op_mul_agg);
+ }
+
+ for(int i = 0; i < m; i++) {
+ for(int j = 0; j < m; j++) {
+ if(i != j) {
+ if(Math.abs(in.getValue(i, j)) > tol)
+ throw new
DMLRuntimeException("QR Eigen Decomposition not converged or contains complex
EVs"
+ + " (position i = " + i
+ ", j = " + j + ")");
+ }
+ }
+ }
+
+ // If complex evals then A is in real Schur-form
+ // 2x2 blocks on diagonal of A correspond to a matrix that has
the same complex evals
+ double[] eval = new double[m];
+ for(int i = 0; i < m; i++) {
+ eval[i] = in.getValue(i, i);
+ }
+
+ double[][] evec =
DataConverter.convertToArray2DRowRealMatrix(Q_prod).getData();
+ return sortEVs(eval, evec);
+ }
+
+ /**
+ * Function to compute the Householder transformation of a Matrix.
+ *
+ * @param in Input Matrix
+ * @return transformed matrix
+ */
+ private static MatrixBlock computeHouseholder(MatrixBlock in) {
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ for(int k = 0; k < m - 2; k++) {
+ MatrixBlock ajk = A_n.slice(0, m - 1, k, k);
+ for(int i = 0; i <= k; i++) {
+ ajk.setValue(i, 0, 0.0);
+ }
+ double alpha = Math.sqrt(ajk.sumSq());
+ double ak1k = A_n.getDouble(k + 1, k);
+ if(ak1k > 0.0)
+ alpha *= -1;
+ double r = Math.sqrt(0.5 * (alpha * alpha - ak1k *
alpha));
+ MatrixBlock v = new MatrixBlock(m, 1, 0.0);
+ v.copy(ajk);
+ v.setValue(k + 1, 0, ak1k - alpha);
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 2 * r, num_Threads);
+ v = v.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock P = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ P.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_add =
InstructionUtils.parseExtendedBinaryOperator("+");
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+
+ MatrixBlock v_t = v.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ v_t = v_t.binaryOperations(op_add, v_t);
+ MatrixBlock v_v_t_2 = A_n.aggregateBinaryOperations(v,
v_t, op_mul_agg);
+ P = P.binaryOperations(op_sub, v_v_t_2);
+ A_n = A_n.aggregateBinaryOperations(P,
A_n.aggregateBinaryOperations(A_n, P, op_mul_agg), op_mul_agg);
+ }
+ return A_n;
+ }
+
+ /**
+ * Sort the eigen values (and vectors) in increasing order (to be
compatible w/ LAPACK.DSYEVR())
+ *
+ * @param eValues Eigenvalues
+ * @param eVectors Eigenvectors
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] sortEVs(double[] eValues, double[][]
eVectors) {
+ int n = eValues.length;
+ for(int i = 0; i < n; i++) {
+ int k = i;
+ double p = eValues[i];
+ for(int j = i + 1; j < n; j++) {
+ if(eValues[j] < p) {
+ k = j;
+ p = eValues[j];
+ }
+ }
+ if(k != i) {
+ eValues[k] = eValues[i];
+ eValues[i] = p;
+ for(int j = 0; j < n; j++) {
+ p = eVectors[j][i];
+ eVectors[j][i] = eVectors[j][k];
+ eVectors[j][k] = p;
+ }
+ }
+ }
+
+ MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues,
true);
+ MatrixBlock evec = DataConverter.convertToMatrixBlock(eVectors);
+ return new MatrixBlock[] {eval, evec};
+ }
+
+ private static boolean isSym(MatrixBlock in) {
Review comment:
1. consider if you want to use it the one place it is in use i commented
that you probably should not. if you decide to throw it away ignore 2.
2. The convertToArray2DRowReadlMatrix call makes this exceedingly slow,
instead use the getDenseDoubleValues() to get a row major double[] that you can
compare in.
##########
File path:
src/test/java/org/apache/sysds/test/component/matrix/EigenDecompTest.java
##########
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.matrix;
+
+import org.apache.sysds.runtime.matrix.data.LibCommonsMath;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+import org.apache.sysds.runtime.util.DataConverter;
+import org.apache.sysds.test.TestUtils;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class EigenDecompTest {
+
+ @Test public void testLanczosSimple() {
+ double tol = 1e-4;
+
+ MatrixBlock in = new MatrixBlock(4, 4, false);
+ double[] a = { 4, 1, -2, 2,
+ 1, 2, 0, 1,
+ -2, 0, 3, -2,
+ 2, 1, -2, -1};
Review comment:
format
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
+ double tol = 1e-10;
+ int m = in.rlen;
+
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_prod.setValue(i, i, 1.0);
+ }
+
+ for(int i = 0; i < num_iterations; i++) {
+ MatrixBlock[] QR = computeQR2(in);
+ Q_prod = Q_prod.aggregateBinaryOperations(Q_prod,
QR[0], op_mul_agg);
+ in = in.aggregateBinaryOperations(QR[1], QR[0],
op_mul_agg);
+ }
+
+ for(int i = 0; i < m; i++) {
+ for(int j = 0; j < m; j++) {
+ if(i != j) {
+ if(Math.abs(in.getValue(i, j)) > tol)
+ throw new
DMLRuntimeException("QR Eigen Decomposition not converged or contains complex
EVs"
+ + " (position i = " + i
+ ", j = " + j + ")");
+ }
+ }
+ }
+
+ // If complex evals then A is in real Schur-form
+ // 2x2 blocks on diagonal of A correspond to a matrix that has
the same complex evals
+ double[] eval = new double[m];
+ for(int i = 0; i < m; i++) {
+ eval[i] = in.getValue(i, i);
Review comment:
here also leverage the double array from the comment above.
##########
File path: src/test/java/org/apache/sysds/test/TestUtils.java
##########
@@ -1802,6 +1802,16 @@ public static MatrixBlock generateTestMatrixBlock(int
rows, int cols, double min
return MatrixBlock.randOperations(rows, cols, sparsity, min,
max, "Uniform", seed);
}
+ public static MatrixBlock generateTestMatrixBlockSym(int rows, int
cols, double min, double max, double sparsity, long seed){
+ MatrixBlock m = MatrixBlock.randOperations(rows, cols,
sparsity, min, max, "Uniform", seed);
+ for(int i = 0; i < rows; i++) {
Review comment:
consider starting from i = 1
##########
File path:
src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java
##########
@@ -295,11 +281,283 @@ private static MatrixBlock
computeMatrixInverse(Array2DRowRealMatrix in) {
* @return matrix block
*/
private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) {
- if ( !in.isSquare() )
- throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a " + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
+ if(!in.isSquare())
+ throw new DMLRuntimeException("Input to cholesky() must
be square matrix -- given: a "
+ + in.getRowDimension() + "x" +
in.getColumnDimension() + " matrix.");
CholeskyDecomposition cholesky = new CholeskyDecomposition(in,
RELATIVE_SYMMETRY_THRESHOLD,
CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD);
RealMatrix rmL = cholesky.getL();
return DataConverter.convertToMatrixBlock(rmL.getData());
}
+
+ /**
+ * Function to perform the Lanczos algorithm and then computes the
Eigendecomposition.
+ * Caution: Lanczos is not numerically stable (see
https://en.wikipedia.org/wiki/Lanczos_algorithm)
+ * Input must be a symmetric (and square) matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException(
+ "Lanczos algorithm and Eigen Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows="
+ in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ if(!isSym(in)) {
+ throw new DMLRuntimeException("Lanczos algorithm can
only be done on a symmetric matrix.");
+ }
+
+ int num_Threads = 1;
+
+ int m = in.getNumRows();
+ MatrixBlock v0 = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock v1 = MatrixBlock.randOperations(m, 1, 1.0, 0.0,
1.0, "UNIFORM", 0xC0FFEE);
+
+ // normalize v1
+ double v1_sum = v1.sum();
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), v1_sum, num_Threads);
+ v1 = v1.scalarOperations(op_div_scalar, new MatrixBlock());
+ UnaryOperator op_sqrt = new
UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT),
num_Threads, true);
+ v1 = v1.unaryOperations(op_sqrt, new MatrixBlock());
+ if(v1.sumSq() != 1.0)
+ throw new DMLRuntimeException("Lanczos algorithm: v1
not correctly normalized");
+
+ MatrixBlock T = new MatrixBlock(m, m, 0.0);
+ MatrixBlock TV = new MatrixBlock(m, 1, 0.0);
+ MatrixBlock w1;
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ TernaryOperator op_minus_mul = new
TernaryOperator(MinusMultiply.getFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+
+ MatrixBlock beta = new MatrixBlock(1, 1, 0.0);
+ for(int i = 0; i < m; i++) {
+ if(i == 0)
+ TV.copy(v1);
+ else
+ TV = TV.append(v1, new MatrixBlock(), true);
+
+ w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg);
+ MatrixBlock w1_t = w1.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock alpha =
w1_t.aggregateBinaryOperations(w1_t, v1, op_mul_agg);
+ if(i < m - 1) {
+ w1 = w1.ternaryOperations(op_minus_mul, v1,
alpha, new MatrixBlock());
+ w1 = w1.ternaryOperations(op_minus_mul, v0,
beta, new MatrixBlock());
+ beta.setValue(0, 0, Math.sqrt(w1.sumSq()));
+ v0.copy(v1);
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(beta.getDouble(0, 0));
+ v1 = w1.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ T.setValue(i + 1, i, beta.getValue(0, 0));
+ T.setValue(i, i + 1, beta.getValue(0, 0));
+ }
+ T.setValue(i, i, alpha.getValue(0, 0));
+ }
+
+ MatrixBlock[] e = multiReturnOperations(T, "eigen");
+ e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg);
+ return e;
+ }
+
+ /**
+ * Function to perform the QR decomposition.
+ * Input must be a square matrix.
+ *
+ * @param in matrix object
+ * @return array of matrix blocks [Q, R]
+ */
+ private static MatrixBlock[] computeQR2(MatrixBlock in) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("QR2 Decomposition can
only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+
+ int num_Threads = 1;
+ int m = in.rlen;
+
+ MatrixBlock A_n = new MatrixBlock(m, m, 0.0);
+ A_n.copy(in);
+
+ MatrixBlock Q_n = new MatrixBlock(m, m, 0.0);
+ for(int i = 0; i < m; i++) {
+ Q_n.setValue(i, i, 1.0);
+ }
+
+ ReorgOperator op_t = new
ReorgOperator(SwapIndex.getSwapIndexFnObject(), num_Threads);
+ AggregateBinaryOperator op_mul_agg =
InstructionUtils.getMatMultOperator(num_Threads);
+ BinaryOperator op_sub =
InstructionUtils.parseExtendedBinaryOperator("-");
+ RightScalarOperator op_div_scalar = new
RightScalarOperator(Divide.getDivideFnObject(), 1, num_Threads);
+ LeftScalarOperator op_mult_2 = new
LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, num_Threads);
+
+ for(int k = 0; k < m; k++) {
+ MatrixBlock z = A_n.slice(k, m - 1, k, k);
+ MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0);
+ uk.copy(z);
+ uk.setValue(0, 0, uk.getValue(0, 0) +
Math.signum(z.getValue(0, 0)) * Math.sqrt(z.sumSq()));
+ op_div_scalar = (RightScalarOperator)
op_div_scalar.setConstant(Math.sqrt(uk.sumSq()));
+ uk = uk.scalarOperations(op_div_scalar, new
MatrixBlock());
+
+ MatrixBlock vk = new MatrixBlock(m, 1, 0.0);
+ vk.copy(k, m - 1, 0, 0, uk, true);
+
+ MatrixBlock vkt = vk.reorgOperations(op_t, new
MatrixBlock(), 0, 0, m);
+ MatrixBlock vkvkt = vk.aggregateBinaryOperations(vk,
vkt, op_mul_agg);
+ MatrixBlock vkvkt2 = vkvkt.scalarOperations(op_mult_2,
new MatrixBlock());
+
+ A_n = A_n.binaryOperations(op_sub,
A_n.aggregateBinaryOperations(vkvkt2, A_n, op_mul_agg));
+ Q_n = Q_n.binaryOperations(op_sub,
Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg));
+ }
+ // Q_n= Q
+ // A_n = R
+ return new MatrixBlock[] {Q_n, A_n};
+ }
+
+ /**
+ * Function that computes the Eigen Decomposition using the QR
algorithm.
+ * Caution: check if the QR algorithm is converged, if not increase
iterations
+ * Caution: if the input matrix has complex eigenvalues results will be
incorrect
+ *
+ * @param in Input matrix
+ * @return array of matrix blocks
+ */
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in) {
+ return computeEigenQR(in, 300);
+ }
+
+ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int
num_iterations) {
+ if(in.getNumRows() != in.getNumColumns()) {
+ throw new DMLRuntimeException("Eigen Decomposition (QR)
can only be done on a square matrix. "
+ + "Input matrix is rectangular (rows=" +
in.getNumRows() + ", cols=" + in.getNumColumns() + ")");
+ }
+ int num_Threads = 1;
Review comment:
same, threads in argument
##########
File path: src/test/java/org/apache/sysds/test/TestUtils.java
##########
@@ -1802,6 +1802,16 @@ public static MatrixBlock generateTestMatrixBlock(int
rows, int cols, double min
return MatrixBlock.randOperations(rows, cols, sparsity, min,
max, "Uniform", seed);
}
+ public static MatrixBlock generateTestMatrixBlockSym(int rows, int
cols, double min, double max, double sparsity, long seed){
Review comment:
add java doc comment here, to make it easy to find this method,(add
keywords or something) we have a bunch of tests using symmetric matrices and
all implement their own, and it would be nice if everyone use the same
generator (this could be it!).
##########
File path:
src/test/java/org/apache/sysds/test/component/matrix/EigenDecompTest.java
##########
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.matrix;
+
+import org.apache.sysds.runtime.matrix.data.LibCommonsMath;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+import org.apache.sysds.runtime.util.DataConverter;
+import org.apache.sysds.test.TestUtils;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class EigenDecompTest {
+
+ @Test public void testLanczosSimple() {
Review comment:
keyword line above
##########
File path:
src/test/java/org/apache/sysds/test/component/matrix/EigenDecompTest.java
##########
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.matrix;
+
+import org.apache.sysds.runtime.matrix.data.LibCommonsMath;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+import org.apache.sysds.runtime.util.DataConverter;
+import org.apache.sysds.test.TestUtils;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class EigenDecompTest {
+
+ @Test public void testLanczosSimple() {
+ double tol = 1e-4;
+
+ MatrixBlock in = new MatrixBlock(4, 4, false);
+ double[] a = { 4, 1, -2, 2,
+ 1, 2, 0, 1,
+ -2, 0, 3, -2,
+ 2, 1, -2, -1};
+ in.init(a, 4, 4);
+ MatrixBlock[] m1 = LibCommonsMath.multiReturnOperations(in,
"eigen");
+ MatrixBlock[] m2 = LibCommonsMath.multiReturnOperations(in,
"eigen_lanczos");
+
+ TestUtils.compareMatrices(m1[0], m2[0], tol, "Result of
eigenvalues of new eigen_lanczos function wrong");
+ testEvecValues(m1[1], m2[1], tol);
+ }
+
+ @Test public void testLanczosRandom() {
Review comment:
put Test keyword on the line above.
##########
File path:
src/test/java/org/apache/sysds/test/component/matrix/EigenDecompTest.java
##########
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.sysds.test.component.matrix;
+
+import org.apache.sysds.runtime.matrix.data.LibCommonsMath;
+import org.apache.sysds.runtime.matrix.data.MatrixBlock;
+import org.apache.sysds.runtime.util.DataConverter;
+import org.apache.sysds.test.TestUtils;
+import org.junit.Test;
+import org.junit.Assert;
+
+public class EigenDecompTest {
+
+ @Test public void testLanczosSimple() {
+ double tol = 1e-4;
+
+ MatrixBlock in = new MatrixBlock(4, 4, false);
+ double[] a = { 4, 1, -2, 2,
+ 1, 2, 0, 1,
+ -2, 0, 3, -2,
+ 2, 1, -2, -1};
+ in.init(a, 4, 4);
+ MatrixBlock[] m1 = LibCommonsMath.multiReturnOperations(in,
"eigen");
+ MatrixBlock[] m2 = LibCommonsMath.multiReturnOperations(in,
"eigen_lanczos");
+
+ TestUtils.compareMatrices(m1[0], m2[0], tol, "Result of
eigenvalues of new eigen_lanczos function wrong");
+ testEvecValues(m1[1], m2[1], tol);
+ }
+
+ @Test public void testLanczosRandom() {
+ double tol = 1e-4;
+
+ MatrixBlock in = TestUtils.generateTestMatrixBlockSym(10, 10,
0.0, 1.0, 1.0, 1);
+ // MatrixBlock in = TestUtils.generateTestMatrixBlockSym(100,
100, 0.0, 1.0, 1.0, 1); // fails
+ long t1 = System.nanoTime();
+ MatrixBlock[] m1 = LibCommonsMath.multiReturnOperations(in,
"eigen");
+ long t2 = System.nanoTime();
+ MatrixBlock[] m2 = LibCommonsMath.multiReturnOperations(in,
"eigen_lanczos");
+ long t3 = System.nanoTime();
+
+ System.out.println(
+ "time eigen: " + (t2 - t1) + " time Lanczos: " + (t3 -
t2) + " Lanczos speedup: " + ((double) (t2 - t1) / (t3 - t2)));
+ TestUtils.compareMatrices(m1[0], m2[0], tol, "Result of
eigenvalues of new eigen_lanczos function wrong");
+ testEvecValues(m1[1], m2[1], tol);
+ }
+
+ @Test public void testQREigenSimple() {
+ double tol = 1e-4;
+
+ MatrixBlock in = new MatrixBlock(4, 4, false);
+ double[] a = { 52, 30, 49, 28,
+ 30, 50, 8, 44,
+ 49, 8, 46, 16,
+ 28, 44, 16, 22};
+ in.init(a, 4, 4);
+
+ MatrixBlock[] m1 = LibCommonsMath.multiReturnOperations(in,
"eigen");
+ MatrixBlock[] m2 = LibCommonsMath.multiReturnOperations(in,
"eigen_qr");
+
+ TestUtils.compareMatrices(m1[0], m2[0], tol, "Result of
eigenvalues of new eigen_qr function wrong");
+ testEvecValues(m1[1], m2[1], tol);
+ }
+
+ @Test public void testQREigenRandom() {
+ double tol = 1e-4;
+
+ MatrixBlock in = TestUtils.generateTestMatrixBlockSym(10, 10,
0.0, 1.0, 1.0, 1);
+ // MatrixBlock in = TestUtils.generateTestMatrixBlock(5, 5,
0.0, 1.0, 1.0, 5); // fails evals correct evecs wrong (evec corresponding to
largest eval correct)
+ // MatrixBlock in = TestUtils.generateTestMatrixBlock(10, 10,
0.0, 1.0, 1.0, 2); // fails complex EVs
+ // MatrixBlock in = TestUtils.generateTestMatrixBlockSym(50,
50, 0.0, 1.0, 1.0, 1); // fails not converged
Review comment:
instead of leaving this outcommented, add more tests, each calling
another method that execute the test with the given argument MatrixBlock "in"
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]