Skip to content

Commit

Permalink
feat: add determinant implementation for matrix (#732)
Browse files Browse the repository at this point in the history
* feat: add determinant implementation for matrix

Added the Determinant method for the Matrix struct under math/matrix.
This method returns the determinant of the matrix.

* fix: determinant spelling and linting

Fixed the spelling error in determinant.go.
Fixed ineffectual error assignment in determinant_test.go.

* test: add test for matrix determinant

Added a test case for single-element matrix.

---------

Co-authored-by: Rak Laptudirm <[email protected]>
  • Loading branch information
Carter907 and raklaptudirm authored Aug 20, 2024
1 parent 662e8e9 commit 24c7f1f
Show file tree
Hide file tree
Showing 2 changed files with 201 additions and 0 deletions.
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()
}
}

0 comments on commit 24c7f1f

Please sign in to comment.