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: Implement Matrix , its Methods and Some Functions including Strassen Matrix Multiplication in Go #662

Merged
merged 20 commits into from
Oct 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
6a9aa2c
feat: Implement Strassen Matrix Multiplication
mohit07raghav19 Sep 30, 2023
b6fa85e
This commit introduces the Strassen matrix multiplication algorithm
mohit07raghav19 Oct 2, 2023
bcc7475
refactor: Rename functions and add implement new functionality
mohit07raghav19 Oct 3, 2023
400e75a
Implement Matrix as a struct type
mohit07raghav19 Oct 4, 2023
b1e4e01
Implement StrassenMatrixMultiply and Matrix Methods
mohit07raghav19 Oct 4, 2023
24fe1a6
Refactor to Use Value Type `Matrix` and Add Benchmarks
mohit07raghav19 Oct 5, 2023
7923f0b
Fixed Golang CI lint errors for file copy_test.go & submatrix_test.go
mohit07raghav19 Oct 6, 2023
f810d01
Merge branch 'TheAlgorithms:master' into master
mohit07raghav19 Oct 11, 2023
3fab688
refactor: Change type variable T to constraints.Integer, rename SameD…
mohit07raghav19 Oct 11, 2023
4d67ed9
refractor: Implement goroutines in Add, CheckEqual, Copy, New, Multip…
mohit07raghav19 Oct 11, 2023
910bb85
Merge branch 'TheAlgorithms:master' into master
mohit07raghav19 Oct 14, 2023
20bb9ee
refractor : Handled error in StrassenMatrixMultiply method instead of…
mohit07raghav19 Oct 14, 2023
dec410b
refractor : Handle errors gracefully by returning an error message in…
mohit07raghav19 Oct 16, 2023
a7e43d2
refractor : Updated the 'copy' function to return an empty matrix if …
mohit07raghav19 Oct 16, 2023
cabf07b
Merge branch 'TheAlgorithms:master' into master
mohit07raghav19 Oct 25, 2023
3c787c7
Updated Documentation in README.md
Oct 25, 2023
fc232f2
refactor: matrix operations to use context and sync packages
mohit07raghav19 Oct 25, 2023
382b49f
refactor: matrix operations to use context and sync packages
mohit07raghav19 Oct 25, 2023
51206f7
Updated Documentation in README.md
Oct 25, 2023
24dcb8f
chore: Add empty commit to trigger actions
mohit07raghav19 Oct 26, 2023
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
58 changes: 44 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.

1. [`Formula`](./math/fibonacci/fibonacci.go#L42): Formula This function calculates the n-th fibonacci number using the [formula](https://en.wikipedia.org/wiki/Fibonacci_number#Relation_to_the_golden_ratio) Attention! Tests for large values fall due to rounding error of floating point numbers, works well, only on small numbers
2. [`Matrix`](./math/fibonacci/fibonacci.go#L15): Matrix This function calculates the n-th fibonacci number using the matrix method. [See](https://en.wikipedia.org/wiki/Fibonacci_number#Matrix_form)
3. [`Recursive`](./math/fibonacci/fibonacci.go#L51): Recursive calculates the n-th fibonacci number recursively by adding the previous two numbers. [See](https://en.wikipedia.org/wiki/Fibonacci_sequence#Definition)
3. [`Recursive`](./math/fibonacci/fibonacci.go#L51): Recursive calculates the n-th fibonacci number recursively by adding the previous two Fibonacci numbers. This algorithm is extremely slow for bigger numbers, but provides a simpler implementation.

---
</details><details>
Expand Down Expand Up @@ -449,34 +449,32 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
4. [`DepthFirstSearchHelper`](./graph/depthfirstsearch.go#L21): No description provided.
5. [`FloydWarshall`](./graph/floydwarshall.go#L15): FloydWarshall Returns all pair's shortest path using Floyd Warshall algorithm
6. [`GetIdx`](./graph/depthfirstsearch.go#L3): No description provided.
7. [`KruskalMST`](./graph/kruskal.go#L87): KruskalMST will return a minimum spanning tree along with its total cost to using Kruskal's algorithm. Time complexity is O(m * log (n)) where m is the number of edges in the graph and n is number of nodes in it.
7. [`KruskalMST`](./graph/kruskal.go#L23): No description provided.
8. [`LowestCommonAncestor`](./graph/lowestcommonancestor.go#L111): For each node, we will precompute its ancestor above him, its ancestor two nodes above, its ancestor four nodes above, etc. Let's call `jump[j][u]` is the `2^j`-th ancestor above the node `u` with `u` in range `[0, numbersVertex)`, `j` in range `[0,MAXLOG)`. These information allow us to jump from any node to any ancestor above it in `O(MAXLOG)` time.
9. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default)
10. [`NewDSU`](./graph/kruskal.go#L34): NewDSU will return an initialised DSU using the value of n which will be treated as the number of elements out of which the DSU is being made
11. [`NewTree`](./graph/lowestcommonancestor.go#L84): No description provided.
10. [`NewTree`](./graph/lowestcommonancestor.go#L84): No description provided.
11. [`NewUnionFind`](./graph/unionfind.go#L24): Initialise a new union find data structure with s nodes
12. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided.
13. [`Topological`](./graph/topological.go#L7): Topological assumes that graph given is valid and that its possible to get a topological ordering. constraints are array of []int{a, b}, representing an edge going from a to b

---
##### Types

1. [`DisjointSetUnion`](./graph/kruskal.go#L29): No description provided.
1. [`Edge`](./graph/kruskal.go#L17): No description provided.

2. [`DisjointSetUnionElement`](./graph/kruskal.go#L21): No description provided.
2. [`Graph`](./graph/graph.go#L9): No description provided.

3. [`Edge`](./graph/kruskal.go#L14): No description provided.
3. [`Item`](./graph/dijkstra.go#L5): No description provided.

4. [`Graph`](./graph/graph.go#L9): No description provided.
4. [`Query`](./graph/lowestcommonancestor_test.go#L9): No description provided.

5. [`Item`](./graph/dijkstra.go#L5): No description provided.
5. [`Tree`](./graph/lowestcommonancestor.go#L25): No description provided.

6. [`Query`](./graph/lowestcommonancestor_test.go#L9): No description provided.
6. [`TreeEdge`](./graph/lowestcommonancestor.go#L12): No description provided.

7. [`Tree`](./graph/lowestcommonancestor.go#L25): No description provided.
7. [`UnionFind`](./graph/unionfind.go#L18): No description provided.

8. [`TreeEdge`](./graph/lowestcommonancestor.go#L12): No description provided.

9. [`WeightedGraph`](./graph/floydwarshall.go#L9): No description provided.
8. [`WeightedGraph`](./graph/floydwarshall.go#L9): No description provided.


---
Expand Down Expand Up @@ -664,6 +662,37 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
21. [`Sin`](./math/sin.go#L9): Sin returns the sine of the radian argument x. [See more](https://en.wikipedia.org/wiki/Sine_and_cosine)
22. [`SumOfProperDivisors`](./math/perfectnumber.go#L17): Returns the sum of proper divisors of inNumber.

---
</details><details>
<summary> <strong> matrix </strong> </summary>

---

##### filename: strassenmatrixmultiply.go description: Implements matrix multiplication using the Strassen algorithm. details: This program takes two matrices as input and performs matrix multiplication using the Strassen algorithm, which is an optimized divide-and-conquer approach. It allows for efficient multiplication of large matrices. author(s): Mohit Raghav(https://github.com/mohit07raghav19) See strassenmatrixmultiply_test.go for test cases

---
##### Functions:

1. [`IsValid`](./math/matrix/isvalid.go#L6): IsValid checks if the input matrix has consistent row lengths.
2. [`New`](./math/matrix/matrix.go#L17): NewMatrix creates a new Matrix based on the provided arguments.
3. [`NewFromElements`](./math/matrix/matrix.go#L43): NewFromElements creates a new Matrix from the given elements.

---
##### Types

1. [`Matrix`](./math/matrix/matrix.go#L10): No description provided.


---
</details><details>
<summary> <strong> matrix_test </strong> </summary>

---

##### Functions:

1. [`MakeRandomMatrix`](./math/matrix/strassenmatrixmultiply_test.go#L105): No description provided.

---
</details><details>
<summary> <strong> max </strong> </summary>
Expand Down Expand Up @@ -1084,6 +1113,7 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.

1. [`CountChars`](./strings/charoccurrence.go#L12): CountChars counts the number of a times a character has occurred in the provided string argument and returns a map with `rune` as keys and the count as value.
2. [`IsIsogram`](./strings/isisogram.go#L34): No description provided.
3. [`IsSubsequence`](./strings/issubsequence.go#L10): Returns true if s is subsequence of t, otherwise return false.

---
</details><details>
Expand Down
64 changes: 64 additions & 0 deletions math/matrix/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package matrix

import (
"context"
"errors"
"sync"
)

// Add adds two matrices.
func (m1 Matrix[T]) Add(m2 Matrix[T]) (Matrix[T], error) {
// Check if the matrices have the same dimensions.
if !m1.MatchDimensions(m2) {
return Matrix[T]{}, errors.New("matrices are not compatible for addition")
}

// Create a new matrix to store the result.
var zeroVal T
result := New(m1.Rows(), m1.Columns(), zeroVal)

ctx, cancel := context.WithCancel(context.Background())
defer cancel() // Make sure it's called to release resources even if no errors

var wg sync.WaitGroup
errCh := make(chan error, 1)

for i := 0; i < m1.rows; i++ {
i := i // Capture the loop variable for the goroutine
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < m1.columns; j++ {
select {
case <-ctx.Done():
return // Context canceled; return without an error
default:
}

sum := m1.elements[i][j] + m2.elements[i][j]
err := result.Set(i, j, sum)
if err != nil {
cancel() // Cancel the context on error
select {
case errCh <- err:
default:
}
return
}
}
}()
}

// Wait for all goroutines to finish
go func() {
wg.Wait()
close(errCh)
}()

// Check for any errors
if err := <-errCh; err != nil {
return Matrix[T]{}, err
}

return result, nil
}
58 changes: 58 additions & 0 deletions math/matrix/add_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package matrix_test

import (
"fmt"
"testing"

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

func TestAdd(t *testing.T) {
// Create two matrices with the same dimensions for addition
m1 := matrix.New(2, 2, 1)
m2 := matrix.New(2, 2, 2)

// Test case 1: Valid matrix addition
addedMatrix, err := m1.Add(m2)
if err != nil {
t.Errorf("Add(m1, m2) returned an error: %v, expected no error", err)
}
expectedMatrix := matrix.New(2, 2, 3)
res := addedMatrix.CheckEqual(expectedMatrix)
if !res {
t.Errorf("Add(m1, m2) returned incorrect result:\n%v\nExpected:\n%v", addedMatrix, expectedMatrix)
}

// Create two matrices with different dimensions for addition
m3 := matrix.New(2, 2, 1)
m4 := matrix.New(2, 3, 2)

// Test case 2: Matrices with different dimensions
_, err2 := m3.Add(m4)
expectedError2 := fmt.Errorf("matrices are not compatible for addition")
if err2 == nil || err2.Error() != expectedError2.Error() {
t.Errorf("Add(m3, m4) returned error: %v, expected error: %v", err2, expectedError2)
}

}

func BenchmarkAddSmallMatrix(b *testing.B) {
m1 := matrix.New(10, 10, 0) // Create a 10x10 matrix with all zeros
m2 := matrix.New(10, 10, 1) // Create a 10x10 matrix with all ones

for i := 0; i < b.N; i++ {
_, _ = m1.Add(m2)
}
}

func BenchmarkAddLargeMatrix(b *testing.B) {
size := 1000 // Choose an appropriate size for your large matrix
m1 := MakeRandomMatrix[int](size, size)
m2 := MakeRandomMatrix[int](size, size)

b.ResetTimer() // Reset the timer to exclude setup time

for i := 0; i < b.N; i++ {
_, _ = m1.Add(m2)
}
}
32 changes: 32 additions & 0 deletions math/matrix/checkequal.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package matrix

// CheckEqual checks if the current matrix is equal to another matrix (m2).
// Two matrices are considered equal if they have the same dimensions and
// all their elements are equal.
func (m1 Matrix[T]) CheckEqual(m2 Matrix[T]) bool {
if !m1.MatchDimensions(m2) {
return false
}

c := make(chan bool)

for i := range m1.elements {
go func(i int) {
for j := range m1.elements[i] {
if m1.elements[i][j] != m2.elements[i][j] {
c <- false
return
}
}
c <- true
}(i)
}

for range m1.elements {
if !<-c {
return false
}
}

return true
}
62 changes: 62 additions & 0 deletions math/matrix/checkequal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package matrix_test

import (
"testing"

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

func TestCheckEqual(t *testing.T) {
// Create two matrices with the same dimensions and equal values
m1 := matrix.New(2, 2, 0)
m2 := matrix.New(2, 2, 0)

// Test case 1: Matrices are equal
equal := m1.CheckEqual(m2)

if !equal {
t.Errorf("CheckEqual(m1, m2) returned false, expected true (matrices are equal)")
}

// Create two matrices with the same dimensions but different values
m3 := matrix.New(2, 2, 1)
m4 := matrix.New(2, 2, 0)

// Test case 2: Matrices are not equal
equal2 := m3.CheckEqual(m4)
if equal2 {
t.Errorf("CheckEqual(m3, m4) returned true, expected false (matrices are not equal)")
}

// Create two matrices with different dimensions
m5 := matrix.New(2, 2, 0)
m6 := matrix.New(2, 3, 0)

// Test case 3: Matrices have different dimensions
equal3 := m5.CheckEqual(m6)

if equal3 {
t.Errorf("CheckEqual(m5, m6) returned true, expected false (matrices are not equal)")
}
}

func BenchmarkCheckEqualSmallMatrix(b *testing.B) {
m1 := matrix.New(10, 10, 0) // Create a 10x10 matrix with all zeros
m2 := matrix.New(10, 10, 0) // Create another 10x10 matrix with all zeros

for i := 0; i < b.N; i++ {
_ = m1.CheckEqual(m2)
}
}

func BenchmarkCheckEqualLargeMatrix(b *testing.B) {
size := 1000 // Choose an appropriate size for your large matrix
m1 := MakeRandomMatrix[int](size, size)
m2 := MakeRandomMatrix[int](size, size)

b.ResetTimer() // Reset the timer to exclude setup time

for i := 0; i < b.N; i++ {
_ = m1.CheckEqual(m2)
}
}
53 changes: 53 additions & 0 deletions math/matrix/copy.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package matrix

import "sync"

func (m Matrix[T]) Copy() (Matrix[T], error) {

rows := m.Rows()
columns := m.Columns()
if rows == 0 || columns == 0 {
return Matrix[T]{}, nil
}
zeroVal, err := m.Get(0, 0) // Get the zero value of the element type
if err != nil {
return Matrix[T]{}, err
}
copyMatrix := New(rows, columns, zeroVal)
var wg sync.WaitGroup
wg.Add(rows)
errChan := make(chan error, 1)

for i := 0; i < rows; i++ {
go func(i int) {
defer wg.Done()
for j := 0; j < columns; j++ {
val, err := m.Get(i, j)
if err != nil {
select {
case errChan <- err:
default:
}
return
}
err = copyMatrix.Set(i, j, val)
if err != nil {
select {
case errChan <- err:
default:
}
return
}
}
}(i)
}

wg.Wait()
close(errChan)

if err, ok := <-errChan; ok {
return Matrix[T]{}, err
}

return copyMatrix, nil
}
Loading