Merge branch 'Matthew-Ellison/created-doublematrix-1644273070004' into develop

This commit is contained in:
2022-02-08 19:52:36 -05:00

View File

@@ -0,0 +1,808 @@
//Matrix/src/main/java/com/mattrixwv/matrix/DoubleMatrix.java
//Mattrixwv
// Created: 02-07-22
//Modified: 02-08-22
package com.mattrixwv.matrix;
import java.util.StringJoiner;
import com.mattrixwv.matrix.exceptions.InvalidCoordinatesException;
import com.mattrixwv.matrix.exceptions.InvalidGeometryException;
import com.mattrixwv.matrix.exceptions.InvalidRowSizeException;
import com.mattrixwv.matrix.exceptions.InvalidScalarException;
public class DoubleMatrix{
protected double[][] grid;
protected double delta;
//Helper functions
protected void setGrid(double[][] grid){
if(grid.length == 0){
grid = new double[0][0];
}
else if(grid[0].length == 0){
grid = new double[grid.length][0];
}
else{
//Make sure all rows are the same length
int length = grid[0].length;
for(int cnt = 1;cnt < grid.length;++cnt){
if(grid[cnt].length != length){
throw new InvalidRowSizeException("All rows in a matrix must be the same size");
}
}
//Copy every element over to a new grid
double[][] newGrid = new double[grid.length][grid[0].length];
for(int rowCnt = 0;rowCnt < grid.length;++rowCnt){
for(int colCnt = 0;colCnt < grid[0].length;++colCnt){
newGrid[rowCnt][colCnt] = grid[rowCnt][colCnt];
}
}
//Save the new grid
this.grid = newGrid;
}
}
protected double[][] copyGrid(){
//Allocate memory for the new grid
double[][] newGrid = new double[grid.length][grid[0].length];
//Copy every element from the current grid to the new one
for(int row = 0;row < grid.length;++row){
for(int col = 0;col < grid[0].length;++col){
newGrid[row][col] = grid[row][col];
}
}
//Return the new grid
return newGrid;
}
protected boolean isSquare(){
if(getNumRows() == 0){
return false;
}
else{
return getNumRows() == getNumCols();
}
}
//Returns a matrix with the supplied row and column removed
protected DoubleMatrix laplaceExpansionHelper(int row, int col){
//Make sure the matrix is square
if(!isSquare()){
throw new InvalidGeometryException("A matrix must be square for you to perform Laplace Expansion");
}
//Make sure the matrix is large enough to have this operation performed
if((getNumRows() <= 1) || (getNumCols() <= 1)){
throw new InvalidGeometryException("A matrix must be at least 2x2 for you to perform Laplace Expansion: " + getNumRows() + "x" + getNumCols());
}
//Make sure the row is valid
if((row < 0) || (row >= getNumRows())){
throw new InvalidCoordinatesException("An invalid row number was supplied: " + row + ". The matrix contains " + getNumRows() + " rows");
}
//Make sure the col is valid
if((col < 0) || (col >= getNumCols())){
throw new InvalidCoordinatesException("An invalid column number was supplied: " + col + ". The matrix contains " + getNumCols() + " columns");
}
//Create a new matrix that is one row and column smaller than the existing row
double[][] newGrid = new double[getNumRows() - 1][getNumCols() - 1];
//Traverse the matrix and set the values in the new matrix, skipping elements in the given row and col
for(int workingRow = 0, newRow = 0;workingRow < getNumRows();++workingRow){
//Skip the row we are expanding
if(workingRow == row){
continue;
}
for(int workingCol = 0, newCol = 0;workingCol < getNumCols();++workingCol){
//Skip the column we are expanding
if(workingCol == col){
continue;
}
newGrid[newRow][newCol] = grid[workingRow][workingCol];
++newCol;
}
++newRow;
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
//Constructors
public DoubleMatrix(){
grid = new double[0][0];
delta = 0.0;
}
public DoubleMatrix(double[][] grid){
setGrid(grid);
delta = 0.0;
}
public DoubleMatrix(DoubleMatrix matrix){
setGrid(matrix.grid);
delta = 0.0;
}
public DoubleMatrix(int rows, int cols, double fill){
if(rows <= 0){
throw new InvalidGeometryException("A filled matrix must have at least 1 row");
}
else if(cols <= 0){
throw new InvalidGeometryException("A filled matrix must have at least 1 column");
}
else{
grid = new double[rows][cols];
for(int row = 0;row < rows;++row){
for(int col = 0;col < cols;++col){
grid[row][col] = fill;
}
}
delta = 0.0;
}
}
//Gets
public double get(int row, int col){
//Make sure the row and column are valid
if(row >= grid.length){
throw new InvalidCoordinatesException("Row cannot be greater than the number of rows");
}
else if(col >= grid[0].length){
throw new InvalidCoordinatesException("Column cannot be greater than the number of columns");
}
//Return the location in the grid
return grid[row][col];
}
public DoubleMatrix getRow(int row){
//Make sure the row number is valid
if((row < 0) || (row >= grid.length)){
throw new InvalidCoordinatesException("The row number " + row + " is not valid");
}
//Generate a copy of the row
double[][] newRow = new double[1][grid[row].length];
for(int col = 0;col < grid[row].length;++col){
newRow[0][col] = grid[row][col];
}
//Return the new matrix
return new DoubleMatrix(newRow);
}
public int getNumRows(){
if(grid == null){
return 0;
}
else{
return grid.length;
}
}
public DoubleMatrix getCol(int col){
//Make sure the column number is valid
if((col < 0) || (grid.length == 0) || (col > grid[0].length)){
throw new InvalidCoordinatesException("The column number " + col + " is not valid");
}
//Generate a copy of the col
double[][] newColumn = new double[grid.length][1];
for(int row = 0;row < grid.length;++row){
newColumn[row][0] = grid[row][col];
}
//Return the new matrix
return new DoubleMatrix(newColumn);
}
public int getNumCols(){
if(grid.length > 0){
return grid[0].length;
}
else{
return 0;
}
}
//Sets
public void set(int row, int col, double value){
//Make sure the row number is valid
if((row < 0) || (row >= grid.length)){
throw new InvalidCoordinatesException("Invalid row number " + row);
}
//Make sure the column number is valid
if((col < 0) || (col >= getNumCols())){
throw new InvalidCoordinatesException("Invalid column number " + col);
}
//Save the element
grid[row][col] = value;
}
public void setRow(int row, double[] elements){
//Make sure the row number is valid
if((row < 0) || (row >= grid.length)){
throw new InvalidCoordinatesException("Invalid row number " + row);
}
//Make sure the number of elements is valid
if(elements.length != getNumCols()){
throw new InvalidGeometryException("Invalid number of elements " + elements.length + " must be " + getNumCols());
}
//Save the elements
for(int col = 0;col < elements.length;++col){
grid[row][col] = elements[col];
}
}
public void setRow(int row, DoubleMatrix matrix){
//Make sure the matrix has a single row
if(matrix.getNumRows() != 1){
throw new InvalidGeometryException("Setting a row by Matrix requires the matrix contain a single row");
}
//Set the row
setRow(row, matrix.grid[0]);
}
public void setCol(int col, double[] elements){
//Make sure the column number is valid
if((col < 0) || (col >= getNumCols())){
throw new InvalidCoordinatesException("Invalid column number " + col);
}
//Make sure the number of elements is valid
if(elements.length != grid.length){
throw new InvalidCoordinatesException("Invalid number of elements " + elements.length + " must be " + grid.length);
}
//Save the elements
for(int row = 0;row < elements.length;++row){
grid[row][col] = elements[row];
}
}
public void setCol(int col, DoubleMatrix matrix){
//Make sure the matrix has a single column
if(matrix.getNumCols() != 1){
throw new InvalidGeometryException("Setting a column by Matrix requires the matrix contain a single column");
}
//Set the column
setCol(col, matrix.getCol(0));
}
//Adds
public void addRow(double[] elements){
//Make sure the number of columns is valid
if((grid.length == 0) || (getNumCols() == elements.length)){
double[][] newGrid = new double[grid.length + 1][elements.length];
//Copy all existing data into the new grid
for(int row = 0;row < grid.length;++row){
newGrid[row] = grid[row];
}
grid = newGrid;
}
else{
throw new InvalidGeometryException("Invalid number of elements " + elements.length + " must be " + getNumCols());
}
//Add all elements to the grid
for(int col = 0;col < elements.length;++col){
grid[grid.length - 1][col] = elements[col];
}
}
public void addRow(DoubleMatrix matrix){
//Make sure the matrix has a single row
if(matrix.getNumRows() != 1){
throw new InvalidGeometryException("Adding a row by Matrix requires the matrix contain a single row");
}
//Add the row
addRow(matrix.grid[0]);
}
public void addCol(double[] elements){
//Make sure the number of rows is valid
if(grid.length == 0){
grid = new double[1][elements.length];
}
else if(grid.length == elements.length){
//Copy all existing data into the new grid
for(int row = 0;row < grid.length;++row){
double[] workingRow = new double[grid[row].length + 1];
for(int workingCol = 0;workingCol < grid[row].length;++workingCol){
workingRow[workingCol] = grid[row][workingCol];
}
grid[row] = workingRow;
}
}
else{
throw new InvalidGeometryException("Invalid number of elements " + elements.length + " must be " + getNumCols());
}
//Add all elements to the grid
for(int row = 0;row < elements.length;++row){
grid[row][grid[row].length - 1] = elements[row];
}
}
public void addCol(DoubleMatrix matrix){
//Make sure the matrix has a single column
if(matrix.getNumCols() != 1){
throw new InvalidGeometryException("Adding a column by Matrix requires the matrix contain a single column");
}
//Add the column
addCol(matrix.getCol(0));
}
public DoubleMatrix appendRight(DoubleMatrix rightSide){
//Make sure the matrices have the same number of rows
if(getNumRows() != rightSide.getNumRows()){
throw new InvalidGeometryException("Invalid number of rows. " + rightSide.getNumRows() + " must be " + getNumRows());
}
//Traverse both matrices and set their values in the new matrix
double[][] newGrid = new double[getNumRows()][getNumCols() + rightSide.getNumCols()];
for(int row = 0;row < getNumRows();++row){
//Set all elements from the current grid's row
for(int col = 0;col < getNumCols();++col){
newGrid[row][col] = grid[row][col];
}
//Set all elements from the right side grid's row
for(int col = 0;col < rightSide.getNumCols();++col){
newGrid[row][getNumCols() + col] = rightSide.grid[row][col];
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix appendBottom(DoubleMatrix rightSide){
//Make sure the matrices have the same number of columns
if(getNumCols() != rightSide.getNumCols()){
throw new InvalidGeometryException("Invalid number of columns. " + rightSide.getNumCols() + " must be " + getNumCols());
}
//Traverse both matrices and set their values in the new matrix
double[][] newGrid = new double[getNumRows() + rightSide.getNumRows()][getNumCols()];
for(int col = 0;col < getNumCols();++col){
//Set all elements from the current grid's column
for(int row = 0;row < getNumRows();++row){
newGrid[row][col] = grid[row][col];
}
//Set all elements from the right side grid's column
for(int row = 0;row < rightSide.getNumRows();++row){
newGrid[getNumRows() + row][col] = rightSide.grid[row][col];
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
//Simple operations
public static DoubleMatrix generateIdentity(int size){
//Make sure the size is valid
if(size > 0){
//Create a grid with the correct size
double[][] newGrid = new double[size][size];
//Go through every element and make sure it is set correctly
for(int row = 0;row < size;++row){
for(int col = 0;col < size;++col){
if(col == row){
newGrid[row][col] = 1;
}
else{
newGrid[row][col] = 0;
}
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
else{
throw new InvalidGeometryException("An identity matrix must have a size > 0");
}
}
public static DoubleMatrix generateFilled(int size, double fill){
//Create a grid with the correct size
double[][] newGrid = new double[size][size];
//Set each element in the grid
for(int row = 0;row < size;++row){
for(int col = 0;col < size;++col){
newGrid[row][col] = fill;
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix add(DoubleMatrix rightSide){
//Make sure the matrices have compatable geometry
if((getNumRows() != rightSide.getNumRows()) || (getNumCols() != rightSide.getNumCols())){
throw new InvalidGeometryException("Both matrices must have the same number of rows and columns: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols());
}
//Create a new grid with the same elements as the current grid
double[][] newGrid = copyGrid();
//Add each element in the righ tmatrix to the corresponding element in the left matrix
for(int row = 0;row < newGrid.length;++row){
for(int col = 0;col < newGrid[0].length;++col){
newGrid[row][col] += rightSide.grid[row][col];
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix add(double scalar){
//Create a new grid with the same elements as the current grid
double[][] newGrid = copyGrid();
//Add the scalar to each element in the grid
for(int row = 0;row < newGrid.length;++row){
for(int col = 0;col < newGrid[0].length;++col){
newGrid[row][col] += scalar;
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix subtract(DoubleMatrix rightSide){
//Make sure the matrices have compatable geometry
if((getNumRows() != rightSide.getNumRows()) || (getNumCols() != rightSide.getNumCols())){
throw new InvalidGeometryException("Both matrices must have the same number of rows and columsn: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols());
}
//Create a new grid with the same elements as the current gird
double[][] newGrid = copyGrid();
//Subtract each element in the righ tmatrix from the corresponding element in the left matrix
for(int row = 0;row < newGrid.length;++row){
for(int col = 0;col < newGrid[0].length;++col){
newGrid[row][col] -= grid[row][col];
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix subtract(double scalar){
//Create a new grid with the same elements as the current grid
double[][] newGrid = copyGrid();
//Subtract the scalar from each element in the grid
for(int row = 0;row < newGrid.length;++row){
for(int col = 0;col < newGrid[0].length;++col){
newGrid[row][col] -= scalar;
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix multiply(DoubleMatrix rightSide){
//Make sure the matrices have compatable geometry
if(getNumCols() != rightSide.getNumRows()){
throw new InvalidGeometryException("The left matrix must have the same number of columns as the right matrix has rows: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols());
}
//Create a new grid with the same elements as the current grid
double[][] newGrid = new double[getNumRows()][rightSide.getNumCols()];
//Multiply each row in the left matrix with each column int he right matrix to come up with the current element
for(int leftRow = 0;leftRow < grid.length;++leftRow){
for(int rightCol = 0;rightCol < rightSide.getNumCols();++rightCol){
//Get a sum of the product of each column in the current row (left) and each row in the current column (right)
double elementProductSum = 0;
for(int incrementCounter = 0;incrementCounter < grid.length;++incrementCounter){
elementProductSum += grid[leftRow][incrementCounter] * rightSide.grid[incrementCounter][rightCol];
}
newGrid[leftRow][rightCol] = elementProductSum;
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix multiply(double scalar){
//Create a new grid with the same elements as the current grid
double[][] newGrid = copyGrid();
//Multiply every element in the grid by the scalar
for(int row = 0;row < grid.length;++row){
for(int col = 0;col < grid[0].length;++col){
newGrid[row][col] *= scalar;
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix pow(int power){
//Make sure the matrix is square so it can be multiplied
if(!isSquare()){
throw new InvalidGeometryException("The matrix must be square to raise it to a power");
}
//Make sure the power is positive
if(power < 0){
throw new InvalidScalarException("The power must be >= 0");
}
else if(power == 0){
return new DoubleMatrix(getNumRows(), getNumCols(), 1.0);
}
//Create a new matrix for the product
DoubleMatrix newMatrix = clone();
//Multiply the current grid power times
for(int currentPower = 1;currentPower < power;++currentPower){
newMatrix = newMatrix.multiply(this);
}
//Return the new grid
return newMatrix;
}
public double dotProduct(DoubleMatrix rightSide){
//Make sure the matrices have compatable geometry
if(getNumCols() != rightSide.getNumRows()){
throw new InvalidGeometryException("The left matrix must have the same number of columns as the right matrix has rows: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols());
}
//Multiply each row in the left matrix with each column in the right matrix to come up with the current element
//?Would be simpler, but slower, to get the product matrix to come up with the current element
double sum = 0;
for(int leftRow = 0;leftRow < grid.length;++leftRow){
for(int rightCol = 0;rightCol < rightSide.getNumCols();++rightCol){
for(int incrementCounter = 0;incrementCounter < grid.length;++incrementCounter){
sum += grid[leftRow][incrementCounter] * rightSide.grid[incrementCounter][rightCol];
}
}
}
//Return the sum
return sum;
}
public DoubleMatrix hadamardProduct(DoubleMatrix rightSide){
//Make sure the matrices have compatable geometry
if((getNumRows() != rightSide.getNumRows()) || (getNumCols() != rightSide.getNumCols())){
throw new InvalidGeometryException("Both matrices must have the same number of rows and columns: " + getNumRows() + "x" + getNumCols() + " + " + rightSide.getNumRows() + "x" + rightSide.getNumCols());
}
//Create a new grid with the same element as the current grid
double[][] newGrid = copyGrid();
//Multiply each element in the right matrix to the corresponding element in the left matrix
for(int row = 0;row < newGrid.length;++row){
for(int col = 0;col < newGrid[0].length;++col){
newGrid[row][col] *= rightSide.grid[row][col];
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
//Complex operations
public DoubleMatrix transpose(){
//Create a new grid
double[][] newGrid = new double[getNumCols()][getNumRows()];
//Traverse every element in the existing grid and add each column to the new grid as a row
for(int col = 0;col < getNumCols();++col){
for(int row = 0;row < getNumRows();++row){
newGrid[col][row] = grid[row][col];
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public double det(){
return determinant();
}
public double determinant(){
//Make sure the matrix is square
if(!isSquare()){
throw new InvalidGeometryException("A matrix must be square for it to have a determinant");
}
//?Don't have to worry about a matrix existing. isSquare takes care of 0x0 matrix
//Determine the formula do use for the determinant
double det = 0;
switch(getNumRows()){
//If the matrix is 1x1 return the number
case 1 : det = grid[0][0]; break;
//If the matrix is 2x2 use the formula
case 2 : {
det = (grid[0][0] * grid[1][1]) - (grid[0][1] * grid[1][0]);
}
break;
//If the matrix is 3x3 use the formula
case 3 : {
det = (grid[0][0] * grid[1][1] * grid[2][2]) + (grid[0][1] * grid[1][2] * grid[2][0]) + (grid[0][2] * grid[1][0] * grid[2][1]) -
(grid[2][0] * grid[1][1] * grid[0][2]) - (grid[2][1] * grid[1][2] * grid[0][0]) - (grid[2][2] * grid[1][0] * grid[0][1]);
}
break;
//If the matrix is larger break it down and try again
default : {
//Find the row/column with the largest number of 0's
int zerosLocation = 0;
int maxNumZeros = 0;
boolean zerosRow = true;
//Check the rows
for(int row = 0;row < getNumRows();++row){
int numZeros = 0;
for(int col = 0;col < getNumCols();++col){
if(grid[row][col] == 0){
++numZeros;
}
}
if(numZeros > maxNumZeros){
maxNumZeros = numZeros;
zerosLocation = row;
zerosRow = true;
}
}
//Check the columns
for(int col = 0;col < getNumCols();++col){
int numZeros = 0;
for(int row = 0;row < getNumRows();++row){
if(grid[row][col] == 0){
++numZeros;
}
}
if(numZeros > maxNumZeros){
maxNumZeros = numZeros;
zerosLocation = col;
zerosRow = false;
}
}
//If the largest number of zeros were found in a row
if(zerosRow){
//Set a variable to make sure the appropriate + or - is applied to the scalar
int multiplier = 1;
if((zerosLocation % 2) == 1){
multiplier = -1;
}
//Go through every column in the optimal row using the formula
for(int col = 0;col < getNumCols();++col){
if(grid[zerosLocation][col] != 0){
det += (multiplier * grid[zerosLocation][col] * laplaceExpansionHelper(zerosLocation, col).determinant());
}
multiplier = -multiplier;
}
}
//If the largest number of zeros were found in a column
else{
//Set a variable to make sure the appropriate + or - is applied to the scalar
int multiplier = 1;
if((zerosLocation % 2) == 1){
multiplier = -1;
}
//Go through every row in the coptimal column using the formula
for(int row = 0;row < getNumRows();++row){
if(grid[row][zerosLocation] != 0){
det += (multiplier * grid[row][zerosLocation] * laplaceExpansionHelper(row, zerosLocation).determinant());
}
multiplier = -multiplier;
}
}
}
}
//Return the determinant
return det;
}
public DoubleMatrix cof(){
return cofactor();
}
public DoubleMatrix cofactor(){
//Make sure the matrix is square
if(!isSquare()){
throw new InvalidGeometryException("A matrix must be square to find the cofactor matrix");
}
//Create a new grid
double[][] newGrid = new double[getNumRows()][getNumCols()];
//If the grid is 1x1 return the grid
if(getNumRows() == 1){
newGrid[0][0] = 1;
}
//Use the formula to find the cofactor matrix
else{
for(int row = 0;row < getNumRows();++row){
double multiplier = ((row % 2) == 0) ? 1.0 : -1.0;
for(int col = 0;col < getNumCols();++col){
newGrid[row][col] = multiplier * laplaceExpansionHelper(row, col).determinant();
multiplier = -multiplier;
}
}
}
//Return the new matrix
return new DoubleMatrix(newGrid);
}
public DoubleMatrix adj(){
return adjoint();
}
public DoubleMatrix adjoint(){
return cofactor().transpose();
}
public DoubleMatrix inverse(){
//Make sure the matrix is square
if(!isSquare()){
throw new InvalidGeometryException("A matrix must be square for it to have an inverse");
}
//Make sure the determinant is not 0
double determinant = determinant();
if(determinant == 0){
throw new InvalidScalarException("The determinant cannot be 0");
}
//Return the new matrix
return adjoint().multiply(1 / determinant);
}
//Object funtions
@Override
public boolean equals(Object rightSide){
if(rightSide.getClass().equals(this.getClass())){
DoubleMatrix rightMatrix = (DoubleMatrix)rightSide;
//Make sure they have the same number of elements
if(getNumRows() != rightMatrix.getNumRows()){
return false;
}
else if(getNumCols() != rightMatrix.getNumCols()){
return false;
}
//Check every element
for(int row = 0;row < getNumRows();++row){
for(int col = 0;col < getNumCols();++col){
if(Math.abs(grid[row][col] - rightMatrix.grid[row][col]) > delta){
return false;
}
}
}
//If false hasn't been returned yet then they are equal
return true;
}
else if(rightSide.getClass().equals(double[][].class)){
double[][] rightMatrix = (double[][])rightSide;
return equals(new DoubleMatrix(rightMatrix));
}
else{
return false;
}
}
public boolean equals(Object rightSide, double delta){
setEqualsDelta(delta);
return equals(rightSide);
}
public void setEqualsDelta(double delta){
this.delta = Math.abs(delta);
}
@Override
public int hashCode(){
return grid.hashCode();
}
@Override
public String toString(){
StringJoiner matrix = new StringJoiner("\n");
for(int rowCnt = 0;rowCnt < getNumRows();++rowCnt){
StringJoiner row = new StringJoiner(",", "[", "]");
for(int colCnt = 0;colCnt < getNumCols();++colCnt){
row.add(Double.toString(grid[rowCnt][colCnt]));
}
matrix.add(row.toString());
}
return matrix.toString();
}
@Override
public DoubleMatrix clone(){
return new DoubleMatrix(grid);
}
}