blob: b89da9bb4e78b80b1fd2c46f8d3587c2173eca9d [file] [log] [blame]
/*
* 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.runtime.matrix.data;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
*
* <p>
* Equals library for MatrixBlocks:
* </p>
*
* <p>
* The implementations adhere to the properties of equals of:
* </p>
*
* <ul>
* <li>Reflective</li>
* <li>Symmetric</li>
* <li>Transitive</li>
* <li>Consistent</li>
* </ul>
*
* <p>
* The equals also is valid if the metadata of number of non zeros are unknown in either input. An unknown number of non
* zero values is indicated by a negative nonzero count in the input matrices.
* </p>
*/
public class LibMatrixEquals {
/** Logger for Equals library */
private static final Log LOG = LogFactory.getLog(LibMatrixEquals.class.getName());
/** first block */
private final MatrixBlock a;
/** second block */
private final MatrixBlock b;
/** Epsilon allowed between the blocks */
private final double eps;
/**
* Private instance of a comparison, constructed to reduce the arguments to method calls.
*
* @param a first block
* @param b second block
*/
private LibMatrixEquals(MatrixBlock a, MatrixBlock b) {
this.a = a;
this.b = b;
this.eps = Double.MIN_VALUE * 1024;
}
/**
* Private instance of a comparison, constructed to reduce the arguments to method calls.
*
* @param a first block
* @param b second block
* @param eps epsilon allowed
*/
private LibMatrixEquals(MatrixBlock a, MatrixBlock b, double eps) {
this.a = a;
this.b = b;
this.eps = eps;
}
/**
* <p>
* Analyze if the two matrix blocks are equivalent, this functions even if the underlying allocation and data
* structure varies.
* </p>
*
* <p>
* The implementations adhere to the properties of equals of:
* </p>
*
* <ul>
* <li>Reflective</li>
* <li>Symmetric</li>
* <li>Transitive</li>
* <li>Consistent</li>
* </ul>
*
* @param a Matrix Block a to compare
* @param b Matrix Block b to compare
* @return If the block are equivalent.
*/
public static boolean equals(MatrixBlock a, MatrixBlock b) {
// Same object
if(a == b)
return true;
return new LibMatrixEquals(a, b).exec();
}
/**
* <p>
* Analyze if the two matrix blocks are equivalent, this functions even if the underlying allocation and data
* structure varies.
* </p>
*
* <p>
* The implementations adhere to the properties of equals of:
* </p>
*
* <ul>
* <li>Reflective</li>
* <li>Symmetric</li>
* <li>Transitive</li>
* <li>Consistent</li>
* </ul>
*
* @param a Matrix Block a to compare
* @param b Matrix Block b to compare
* @param eps Epsilon to allow between values
* @return If the block are equivalent.
*/
public static boolean equals(MatrixBlock a, MatrixBlock b, double eps) {
// Same object
if(a == b)
return true;
return new LibMatrixEquals(a, b, eps).exec();
}
/**
* Execute the comparison
*
* @return if the blocks are equivalent
*/
private boolean exec() {
if(isMetadataDifferent())
return false;
else if(a.isEmpty() && b.nonZeros != -1)
return b.isEmpty();
else if(b.isEmpty() && a.nonZeros != -1)
return false;
else if(a.denseBlock != null && b.denseBlock != null)
return a.denseBlock.equals(b.denseBlock, eps);
else if(a.sparseBlock != null && b.sparseBlock != null)
return a.sparseBlock.equals(b.sparseBlock, eps);
else if(a.sparseBlock != null && b.denseBlock != null && b.denseBlock.isContiguous())
return a.sparseBlock.equals(b.denseBlock.values(0), b.getNumColumns(), eps);
else if(b.sparseBlock != null && a.denseBlock != null && a.denseBlock.isContiguous())
return b.sparseBlock.equals(a.denseBlock.values(0), a.getNumColumns(), eps);
return genericEquals();
}
/**
* Compare metadata, and return true if metadata is different
*
* @param a MatrixBlock a
* @param b MatrixBlock b
* @return If the metadata was comparable
*/
private boolean isMetadataDifferent() {
boolean diff = false;
diff |= a.getNumRows() != b.getNumRows();
diff |= a.getNumColumns() != b.getNumColumns();
final long nnzA = a.getNonZeros();
final long nnzB = b.getNonZeros();
diff |= nnzA != -1 && nnzB != -1 && nnzA != nnzB;
return diff;
}
/**
* Generic implementation to cover all cases. But it is slow in most.
*
* @return if the matrices are equivalent.
*/
private boolean genericEquals() {
LOG.warn("Using generic equals, potential optimizations are possible");
final int rows = a.getNumRows();
final int cols = a.getNumColumns();
for(int i = 0; i < rows; i++)
for(int j = 0; j < cols; j++)
if(Math.abs(a.get(i, j) - b.get(i, j)) > eps)
return false;
return true;
}
}