/**
* Copyright (C) 2012 J.W.Marsden <jmarsden@plural.cc>
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package cc.plural.math;
import cc.plural.math.linear.LUDecompositionF;
/**
* Abstract m by n Float Matrix in the following form.
*
* Provides static methods to construct various Matrix instances.
*
* @author j.w.marsden@gmail.com
*/
public abstract class MatrixF extends Matrix {
/**
* Constructor for MatrixF. You must promise to set m and n
*/
public MatrixF() {
}
/**
* Default constructor to specify the dimensions of the m by n MatrixF
*
* @param m rows in the MatrixF.
* @param n columns in the MatrixF.
*/
public MatrixF(int m, int n) {
this.m = m;
this.n = n;
}
/**
* Constructor to create a MatrixF with the given data set.
*
* @param data datum for the matrix.
*/
public MatrixF(Float[][] data) {
m = data.length;
n = data[0].length;
this.setData(data);
}
/**
* Retrieve the data from the MatrixF. This will be unsupported on some
* implementations.
*
* @return Float[][] data from this MatrixF.
*/
public abstract Float[][] getData();
public abstract Float getData(int i, int j);
/**
* Sets the data into the Matrix. This will be unsupported on some
* implementations.
*/
public abstract void setData(Number[][] data);
public abstract void setData(int i, int j, Number data);
/**
* Retrieve the data for a row from the MatrixF.
*
* @param i the row to retrieve (0 <= i < m)
* @return Float[] the row data
*/
public abstract Float[] getRow(int i);
/**
* Retrieve the data for a column from the MatrixF. It is worth noting that
* this data is never mutable.
*
* @param i the row to retrieve (0 <= i < n)
* @return Float[] the column data
*/
public Float[] getColumn(int i) {
if (i >= n) {
throw new IndexOutOfBoundsException();
}
Float[] result = new Float[m];
for (int j = 0; j < m; j++) {
result[j] = getData(j, i);
}
return result;
}
/**
* Adds a Matrix to this instance. Matrix A + Matrix B = Matrix C.
*
* @param b Matrix B.
* @return Matrix Matrix C.
*/
public MatrixF add(Matrix b) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
setData(i, j, getData(i, j).floatValue() + b.getData(i, j).floatValue());
}
}
return this;
}
public MatrixF add(Matrix b, Matrix c) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
c.setData(i, j, getData(i, j).floatValue() + b.getData(i, j).floatValue());
}
}
return this;
}
/**
* Subtracts a Matrix from this instance. Matrix A - Matrix B = Matrix C.
*
* @param b Matrix B.
* @return Matrix Matrix C.
*/
public MatrixF subtract(Matrix b) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
setData(i, j, getData(i, j).floatValue() - b.getData(i, j).floatValue());
}
}
return this;
}
public MatrixF subtract(Matrix b, Matrix c) {
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
c.setData(i, j, getData(i, j).floatValue() - b.getData(i, j).floatValue());
}
}
return this;
}
/**
* Multiplies a Matrix to this instance. Matrix A * Matrix B = Matrix C.
*
* @param b Matrix B.
* @return Matrix Matrix C.
*/
public MatrixF multiply(Matrix b) {
throw new UnsupportedOperationException();
}
public MatrixF multiply(Matrix b, Matrix c) {
throw new UnsupportedOperationException();
}
/**
* Returns the transpose of this instance. Matrix A' = transpose(Matrix A)
*
* @return Matrix A' which is a transpose of this instance.
*/
public abstract MatrixF transpose();
/**
* Adds a scalar value to this Matrix instance. Matrix C = C(c[i,j]) =
* a[i,j]+v
*
* @param v Value to add
* @return Matrix Matrix C
*/
public abstract MatrixF addScalar(Number v);
public abstract MatrixF addScalar(Number v, Matrix c);
/**
* Adds a scalar value to this Matrix instance. Matrix C = C(c[i,j]) =
* a[i,j]-v
*
* @param v Value to subtract
* @return Matrix Matrix C
*/
public abstract MatrixF subtractScalar(Number v);
public abstract MatrixF subtractScalar(Number v, Matrix c);
/**
* Adds a scalar value to this Matrix instance. Matrix C = C(c[i,j]) =
* a[i,j]*v
*
* @param v Value to multiply
* @return Matrix Matrix C
*/
public abstract MatrixF multiplyScalar(Number v);
public abstract MatrixF multiplyScalar(Number v, Matrix c);
/**
* Adds a scalar value to this Matrix instance. Matrix C = C(c[i,j]) =
* a[i,j]/v
*
* @param v Value to divide
* @return Matrix Matrix C
*/
public abstract MatrixF divideScalar(Number v);
public abstract MatrixF divideScalar(Number v, Matrix c);
public abstract VectorF transformVector(VectorF v);
/**
* Return the invert of the Matrix A.
*
* @return Matrix A^-1
*/
public abstract MatrixF invert();
/**
* Matrix Solver in the form A*X=B where A is this Matrix and X is the
* solutions.
*
* @param b Matrix B
* @return Solution Matrix X
*/
public MatrixF solve(Matrix b) {
if (m != n || b.getM() != n || b.getN() != 1) {
throw new RuntimeException("Incorrect matrix dimensions.");
}
for (int i = 0; i < n; i++) {
int max = i;
for (int j = i + 1; j < n; j++) {
if (Math.abs(getData(j, i)) > Math.abs(getData(max, i))) {
max = j;
}
}
/**
* Swap i and max
*/
float tmp;
for (int j = 0; j < n; j++) {
tmp = getData(i, j).floatValue();
setData(i, j, getData(max, j));
setData(max, j, tmp);
}
for (int j = 0; j < b.getN(); j++) {
tmp = b.getData(i, j).floatValue();
b.setData(i, j, b.getData(max, j));
b.setData(max, j, tmp);
}
/**
* Check Singular
*/
if (getData(i, i) == 0) {
throw new RuntimeException("Matrix is singular.");
}
/**
* Pivot B
*/
for (int j = i + 1; j < n; j++) {
b.setData(j, 0, (b.getData(j, 0).floatValue() - (b.getData(i, 0).floatValue() * getData(j, i).floatValue() / getData(i, i).floatValue())));
}
/**
* Pivot A
*/
for (int j = i + 1; j < n; j++) {
float f = getData(j, i) / getData(i, i);
for (int k = i + 1; k < n; k++) {
setData(j, k, (getData(j, k) - getData(i, k) * f));
}
setData(j, i, 0);
}
}
MatrixF x = MatrixF.empty(n, 1);
for (int j = n - 1; j >= 0; j--) {
double v = 0;
for (int k = j + 1; k < n; k++) {
v += getData(j, k).floatValue() * x.getData(k, 0).floatValue();
}
x.setData(j, 0, ((b.getData(j, 0).floatValue() - v) / getData(j, j).floatValue()));
}
return x;
}
@Override
public Matrix solve(Matrix b, Matrix c) {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Calculates the determinant of the MatrixF if it is square (n=m).
* Currently this handles cases where n < 3 internally and uses
* LUDecompositionF for larger instances. @return double detminant of the
* MatrixF.
*/
public Float determinant() {
if (n != m) {
throw new RuntimeException("Not a square matrix.");
}
Float[][] data = getData();
if (n == 1) {
return data[0][0];
} else if (n == 2) {
return data[0][0] * data[1][1]
- data[0][1] * data[1][0];
} else if (n == 3) {
return data[0][0] * data[1][1] * data[2][2]
+ data[0][1] * data[1][2] * data[2][0]
+ data[0][2] * data[1][0] * data[2][1]
- data[0][0] * data[1][2] * data[2][1]
- data[0][1] * data[1][0] * data[2][2]
- data[0][2] * data[1][1] * data[2][0];
} else {
return (new LUDecompositionF(this)).determinant();
}
}
@Override
public Number determinant(Number r) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Matrix getSubMatrix(int mi, int mj, int ni, int nj) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
public Matrix getSubMatrix(int mi, int mj, int ni, int nj, Matrix r) {
throw new UnsupportedOperationException("Not supported yet.");
}
/**
* Convert the MatrixF to a String
*
* @return String MatrixF as String
*/
@Override
public String toString() {
String dataString = "{";
for (int i = 0; i < m; i++) {
dataString += "{";
for (int j = 0; j < n; j++) {
dataString += getData(i, j) + ((j < n - 1) ? "F," : "F");
}
dataString += "}" + ((i < m - 1) ? "," : "");
}
dataString += "}";
return String.format("%s %s", super.toString(), dataString);
}
public static MatrixF empty(int n, int i) {
throw new UnsupportedOperationException("Not supported yet.");
}
}