Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add determinant implementation for matrix #732

Merged
merged 4 commits into from
Aug 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions math/matrix/determinant.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// determinant.go
// description: This method finds the determinant of a matrix.
// details: For a theoretical explanation as for what the determinant
// represents, see the [Wikipedia Article](https://en.wikipedia.org/wiki/Determinant)
// author [Carter907](https://github.com/Carter907)
// see determinant_test.go

package matrix

import (
"errors"
)

// Calculates the determinant of the matrix.
// This method only works for square matrices (e.i. matrices with equal rows and columns).
func (mat Matrix[T]) Determinant() (T, error) {

var determinant T = 0
var elements = mat.elements
if mat.rows != mat.columns {

return 0, errors.New("Matrix rows and columns must equal in order to find the determinant.")
}

// Specify base cases for different sized matrices.
switch mat.rows {
case 1:
return elements[0][0], nil
case 2:
return elements[0][0]*elements[1][1] - elements[1][0]*elements[0][1], nil
default:
for i := 0; i < mat.rows; i++ {

var initialValue T = 0
minor := New(mat.rows-1, mat.columns-1, initialValue)
// Fill the contents of minor excluding the 0th row and the ith column.
for j, minor_i := 1, 0; j < mat.rows && minor_i < minor.rows; j, minor_i = j+1, minor_i+1 {
for k, minor_j := 0, 0; k < mat.rows && minor_j < minor.rows; k, minor_j = k+1, minor_j+1 {
if k != i {
minor.elements[minor_i][minor_j] = elements[j][k]
} else {
minor_j-- // Decrement the column of minor to account for skipping the ith column of the matrix.
}
}
}

if i%2 == 0 {
minor_det, _ := minor.Determinant()

determinant += elements[0][i] * minor_det
} else {
minor_det, _ := minor.Determinant()

determinant += elements[0][i] * minor_det
}
}
return determinant, nil
}
}
142 changes: 142 additions & 0 deletions math/matrix/determinant_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
package matrix_test

import (
"errors"
"math"
"math/rand"
"testing"

"github.com/TheAlgorithms/Go/math/matrix"
)

// Test different matrix contents
func TestMatrixDeterminant(t *testing.T) {
// Find Determinant of a 2 by 2 matrix.
matrix1, err := matrix.NewFromElements([][]int{
{3, 8},
{4, 6},
})
if err != nil {
t.Fatalf("Error creating 3 by 3 matrix: %v", err)
}
determinant, err := matrix1.Determinant()
if err != nil {
t.Fatalf("Error returned from 3 by 3 matrix: %v", err)
}
if determinant != -14 {
t.Fatalf("Determinant returned for a 3 by 3 matrix was %d; wanted -14", determinant)
}

// Find Dertminant of a 1 by 1 matrix
expectedValue := rand.Intn(math.MaxInt)
matrix2, err := matrix.NewFromElements([][]int{
{expectedValue},
})
if err != nil {
t.Fatalf("Error creating 1 by 1 matrix: %v", err)
}
determinant, err = matrix2.Determinant()
if err != nil {
t.Fatalf("Error returned from 1 by 1 matrix: %v", err)
}
if determinant != expectedValue {
t.Fatalf("Determinant returned for a 1 by 1 matrix was %d; wanted %d", determinant, expectedValue)
}

}

func TestEmptyMatrix(t *testing.T) {
emptyElements := [][]int{}
matrix, err := matrix.NewFromElements(emptyElements)

if err != nil {
t.Fatalf("Error creating Matrix with empty elements: %v", err)
}

determinant, err := matrix.Determinant()

if err != nil {
t.Fatalf("Determinant returned an error for empty matrix: %v", err)
}

// Check that 0 is returned from an empty matrix.
expectedValue := 0
if determinant != expectedValue {
t.Errorf("Determinant returned from empty matrix was %d; wanted %d", determinant, expectedValue)
}

}

func TestNonSquareMatrix(t *testing.T) {
// Creating non-square matrix for testing.
initialValue := 0
initialRows := 4
initialCols := 2

nonSquareMatrix := matrix.New(initialRows, initialCols, initialValue)

determinant, err := nonSquareMatrix.Determinant()
// Check if non square matrix returns an error.
if err == nil {
t.Fatalf("No error was returned for a non-square matrix")
}

// Check if the correct error was returned.
expectedError := errors.New("Matrix rows and columns must equal in order to find the determinant.")

if err.Error() != expectedError.Error() {
t.Errorf("Error returned from non-square matrix was \n\"%v\"; \nwanted \n\"%v\"", err, expectedError)
}

// Check if the determinant of the non-square matrix is 0.
if determinant != 0 {
t.Errorf("Determinant of non-square matrix was not 0 but was %d", determinant)
}

}

// Test matrix returned from matrix.New
func TestDefaultMatrix(t *testing.T) {
initialValue := 0
initialRows := 3
initialCols := 3
defaultMatrix := matrix.New(initialRows, initialCols, initialValue)

determinant, err := defaultMatrix.Determinant()

if err != nil {
t.Fatalf("Error finding the determinant of 3 by 3 default matrix: %v.", err)
}
expectedValue := 0
if determinant != expectedValue {
t.Errorf("Determinant of the default matrix with an initial value 0 was %d; wanted %d.", initialValue, expectedValue)
}
}

// Benchmark a 3 by 3 matrix for computational throughput
func BenchmarkSmallMatrixDeterminant(b *testing.B) {
// Create a 3 by 3 matrix for benchmarking
rows := 3
columns := 3
initialValue := 0
matrix := matrix.New(rows, columns, initialValue)

for i := 0; i < b.N; i++ {
_, _ = matrix.Determinant()
}
}

// Benchmark a 10 by 10 matrix for computational throughput.
func BenchmarkMatrixDeterminant(b *testing.B) {
// Create a 10 by 10 matrix for benchmarking
rows := 10
columns := 10
initialValue := 0
matrix := matrix.New(rows, columns, initialValue)

b.ResetTimer()

for i := 0; i < b.N; i++ {
_, _ = matrix.Determinant()
}
}
Loading