Matrix Slicing
Matrix Slicing(Extracting Parts of Matrix) Matrix can be indexed to extract/set a single element, a row, a column, or a submatrix.
Extracting/Setting part of a Vector
// A Vector can be indexed with one index
RowVec R1 = Rand(4);
Console.WriteLine($"R1 = {R1}");
Console.WriteLine($"R1[2] = {R1[2]}");
ColVec C1 = Rand(8);
Console.WriteLine($"C1 = {C1}");
Console.WriteLine($"C1[5] = {C1[5]}");
Ouput
R1 =
0.6101 0.2624 0.5934 0.2917
R1[2] = 0.5934211063140448
C1 =
0.0203
0.8464
0.8927
0.5296
0.1157
0.8461
0.0052
0.0303
C1[5] = 0.8461125105197019
Extracting part of a Matrix
Matrix A = new double[,]
{
{ 8, 1, 6, 1, 16 },
{ 3, 5, 6, 2, 15 },
{ 4, 7, 2, 1, 14 }
};
//Print the matrix
Console.WriteLine($"A = {A}");
// Extract single element using subscript
Console.WriteLine($"A[1,2] = {A[1, 2]}");
// Extract single element using index
Console.WriteLine($"A[5] = {A[5]}");
// Extract multiple elements using index
Console.WriteLine($"A[2..5] = {A[2..5]}");
// Extract multiple elements using subscript along a row
Console.WriteLine($"A[1, 2..4] = {A[1, 2..4]}");
// Extract multiple elements using subscript along a col
Console.WriteLine($"A[0..3, 3] = {A[0..3, 3]}");
// Extract submatrix elements
Console.WriteLine($"A[0..3, 1..3] = {A[0..3, 1..3]}");
// Extract single row
Console.WriteLine($"A[1, ..] = {A[1, ..]}");
// Extract multiple rows
Console.WriteLine($"A[1..3, ..] = {A[1..3, ..]}");
//
Ouput
A =
8 1 6 1 16
3 5 6 2 15
4 7 2 1 14
A[1,2] = 6
A[5] = 7
A[2..5] =
4
1
5
A[1, 2..4] =
6 2
A[0..3, 3] =
1
2
1
A[0..3, 1..3] =
1 6
5 6
7 2
A[1, ..] =
3 5 6 2 15
A[1..3, ..] =
3 5 6 2 15
4 7 2 1 14
Setting Portions of a Matrix
Matrix A = new double[,]
{
{ 8, 1, 6, 1, 16 },
{ 3, 5, 6, 2, 15 },
{ 4, 7, 2, 1, 14 }
};
// set single element using subscript
Console.WriteLine($"A = {A}");
A[1, 2] = 125;
Console.WriteLine($"A = {A}");
// set single element using index
A[5] = 110;
Console.WriteLine($"A = {A}");
// set multiple elements using index
A[2..5] = new double[,] { { 10, 15, 20 } };
Console.WriteLine($"A = {A}");
// set multiple elements using subscript along a row
A[1, 2..4] = new double[] { 150, 200 };
Console.WriteLine($"A = {A}");
// set multiple elements using subscript along a col
A[0..3, 3] = new double[] { 100, 150, 200 };
Console.WriteLine($"A = {A}");
// set submatrix elements
Indexer i = new(0, 3), j = new(1, 3);
A[0..3, 1..3] = new double[,]
{
{ 100, 150 },
{ 100, 150 },
{ 100, 150 }
};
Console.WriteLine($"A = {A}");
// set single row
A[1, ..] = new double[] { 1, 2, 3, 4, 5 };
Console.WriteLine($"A = {A}");
// set multiple rows
A[1..3, ..] = Rand(2, 5);
Console.WriteLine($"A = {A}");
Ouput
A =
8 1 6 1 16
3 5 6 2 15
4 7 2 1 14
A =
8 1 6 1 16
3 5 125 2 15
4 7 2 1 14
A =
8 1 6 1 16
3 5 125 2 15
4 110 2 1 14
A =
8 15 6 1 16
3 20 125 2 15
10 110 2 1 14
A =
8 15 6 1 16
3 20 150 200 15
10 110 2 1 14
A =
8 15 6 100 16
3 20 150 150 15
10 110 2 200 14
A =
8 100 150 100 16
3 100 150 150 15
10 100 150 200 14
A =
8 100 150 100 16
1 2 3 4 5
10 100 150 200 14
A =
8.0000 100.0000 150.0000 100.0000 16.0000
0.6966 0.6182 0.8175 0.6807 0.1420
0.4268 0.7315 0.7878 0.9613 0.5538
Application of Matrix Slicing: Strassen Multiplication
Strassen’s Matrix Multiplication Overview ——–
Inventor: Volker Strassen, 1969
Purpose: Improve efficiency of matrix multiplication beyond the classical cubic-time algorithm.
Key Idea: Replace some multiplications with additions/subtractions by reorganizing computation.
Standard vs. Strassen Multiplication
Feature |
Standard Algorithm |
Strassen Algorithm |
|---|---|---|
Approach |
Direct row-by-column multiplication |
Divide-and-conquer with recursive submatrices |
Multiplications for 2×2 matrices |
8 |
7 |
Additions/Subtractions |
4 |
18 |
Time Complexity |
O(n^3) |
O(n^(log2 7)) ≈ O(n^2.81) |
Best Use Case |
Small matrices |
Large matrices |
Algorithm Steps
Divide: Split each n×n matrix into four (n/2)×(n/2) submatrices
Compute 7 products (instead of 8)
Combine results to form the product matrix
** Return the result
Advantages
Fewer multiplications → faster for large matrices.
Foundation for advanced algorithms (e.g., Coppersmith–Winograd).
Works over any ring (addition and multiplication defined).
Limitations
Overhead of additions makes it slower for small matrices.
Numerical stability issues (rounding errors).
Not optimal compared to modern optimized libraries (BLAS, GPU-based methods).
Applications
-Computer graphics (large matrix transformations). -Scientific computing (linear algebra problems). -Machine learning (deep learning frameworks).
static Matrix Strass(Matrix A, Matrix B)
{
if (A.Cols != B.Rows)
throw new Exception("Matrices are not conformable for multiplication");
if (A.Cols <= 2)
return A * B;
else
{
// get matrix size
int N = A.Cols / 2;
// Step 1: Divide matrices into quadrants
Matrix A11 = A[..N, ..N], A12 = A[..N, N..],
A21 = A[N.., ..N], A22 = A[N.., N..],
B11 = B[..N, ..N], B12 = B[..N, N..],
B21 = B[N.., ..N], B22 = B[N.., N..],
// Step 2: Calculate the 7 Strassen products (M1 through M7)
M1 = Strass(A11 + A22, B11 + B22),
M2 = Strass(A21 + A22, B11),
M3 = Strass(A11, B12 - B22),
M4 = Strass(A22, B21 - B11),
M5 = Strass(A11 + A12, B22),
M6 = Strass(A21 - A11, B11 + B12),
M7 = Strass(A12 - A22, B21 + B22),
// Step 3: Combine products into the quadrants of C
C11 = M1 + M4 - M5 + M7,
C12 = M3 + M5,
C21 = M2 + M4,
C22 = M1 - M2 + M3 + M6,
// Step 4: Assemble the final matrix
C = new Matrix[,]
{
{ C11, C12 },
{ C21, C22 }
};
return C;
}
}
Matrix A = Rand(8, 8), B = Rand(8, 8), C = Strass(A, B), D = A * B;
Console.WriteLine($"A = \n{A}");
Console.WriteLine($"B = \n{B}");
Console.WriteLine($"C = \n{C}");
Console.WriteLine($"D = \n{D}");
Ouput
A =
0.8839 0.7641 0.5702 0.7626 0.4483 0.7004 0.2154 0.0656
0.7984 0.0812 0.8509 0.5595 0.5520 0.7924 0.1532 0.7032
0.6139 0.8412 0.5487 0.5531 0.4775 0.6884 0.4546 0.6187
0.9744 0.0016 0.2262 0.2911 0.9404 0.9404 0.7957 0.1752
0.7995 0.1130 0.2070 0.7442 0.9375 0.5496 0.9865 0.2307
0.7290 0.0865 0.8212 0.1263 0.5410 0.2072 0.1253 0.2361
0.3186 0.9223 0.0318 0.4450 0.7310 0.7903 0.9932 0.6844
0.3274 0.2117 0.8262 0.1324 0.6103 0.6928 0.7552 0.3124
B =
0.0466 0.6668 0.8503 0.5785 0.5843 0.3644 0.4449 0.7652
0.2332 0.3216 0.3984 0.7952 0.3773 0.4904 0.2687 0.0696
0.1730 0.5850 0.0928 0.2556 0.7053 0.1374 0.8529 0.0900
0.9815 0.2466 0.2016 0.4254 0.3379 0.8938 0.7303 0.8364
0.6252 0.4769 0.7578 0.8699 0.5785 0.6805 0.9948 0.1881
0.2913 0.1628 0.9972 0.8719 0.0715 0.4451 0.1745 0.4288
0.8328 0.4346 0.3502 0.4073 0.9281 0.5159 0.0095 0.1408
0.5653 0.5774 0.0125 0.5687 0.8525 0.8207 0.4439 0.2132
C =
1.7672 1.8161 2.3770 2.7147 2.0300 2.2385 2.2411 1.8476
1.8535 2.0591 2.1739 2.6152 2.4040 2.3322 2.5123 1.7763
2.0900 2.0320 2.2348 2.9522 2.5249 2.5797 2.2451 1.6213
1.9941 1.9029 2.8400 2.8082 2.3269 2.2598 2.0243 1.7390
2.5280 1.9727 2.5009 2.7496 2.6012 2.6213 2.2464 1.8609
0.9566 1.5080 1.4194 1.5907 1.7259 1.2526 1.8205 1.0020
2.5735 1.9416 2.4294 3.2339 2.6916 2.8938 1.9200 1.4453
1.7263 1.7149 1.8878 2.2454 2.2685 1.8246 1.8778 1.0352
D =
1.7672 1.8161 2.3770 2.7147 2.0300 2.2385 2.2411 1.8476
1.8535 2.0591 2.1739 2.6152 2.4040 2.3322 2.5123 1.7763
2.0900 2.0320 2.2348 2.9522 2.5249 2.5797 2.2451 1.6213
1.9941 1.9029 2.8400 2.8082 2.3269 2.2598 2.0243 1.7390
2.5280 1.9727 2.5009 2.7496 2.6012 2.6213 2.2464 1.8609
0.9566 1.5080 1.4194 1.5907 1.7259 1.2526 1.8205 1.0020
2.5735 1.9416 2.4294 3.2339 2.6916 2.8938 1.9200 1.4453
1.7263 1.7149 1.8878 2.2454 2.2685 1.8246 1.8778 1.0352
Logical Indexing
Logical indexing is a powerful feature in Sepal Solver that allows you to access or modify matrix elements based on specific conditions rather than explicit coordinates. If you are familiar with MATLAB or NumPy, this syntax will feel natural.
Instead of using integer coordinates (e.g., A[0, 5]), you pass a boolean condition into the indexer. Sepal Solver evaluates this condition across the entire matrix to create a mask, then applies the operation only to the elements where the condition is true.
To extract elements that meet a specific criterion, use relational operators directly within the brackets. This returns a vector containing all matching values.
Matrix A = Rand(5, 6);
Console.WriteLine(A);
// Extract all values greater than 0.5
var L = A[A > 0.5];
Console.WriteLine(L);
Ouput
0.0582 0.4340 0.3553 0.7183 0.1752 0.1588
0.6048 0.7098 0.7288 0.6846 0.7890 0.5814
0.3696 0.1447 0.5415 0.1784 0.0254 0.5026
0.7942 0.3547 0.1016 0.4599 0.7950 0.6230
0.9701 0.0593 0.1765 0.6953 0.4774 0.5905
0.6048
0.7942
0.9701
0.7098
0.7288
0.5415
0.7183
0.6846
0.6953
0.7890
0.7950
0.5814
0.5026
0.6230
0.5905
Logical indexing is most effective when performing bulk updates. You can set values for specific elements without affecting the rest of the matrix.
Matrix A = Rand(5, 6);
A *= 10;
Console.WriteLine(A);
// Set all elements less than 5 to zero
A[A < 5] = 0;
Console.WriteLine(A);
// Replace specific "masquerading" integers or outliers
A[A > 9] = double.NaN;
Console.WriteLine(A);
Ouput
0.3157 6.9009 2.7361 7.5278 8.8312 1.7940
3.2904 0.7512 7.6752 1.2288 5.5011 7.1961
3.5678 5.6637 9.2504 4.4044 1.0342 4.5853
3.9530 6.5408 7.1609 0.8664 8.3373 7.2224
3.0346 7.3378 8.3331 3.6896 7.3221 0.5614
0.0000 6.9009 0.0000 7.5278 8.8312 0.0000
0.0000 0.0000 7.6752 0.0000 5.5011 7.1961
0.0000 5.6637 9.2504 0.0000 0.0000 0.0000
0.0000 6.5408 7.1609 0.0000 8.3373 7.2224
0.0000 7.3378 8.3331 0.0000 7.3221 0.0000
0.0000 6.9009 0.0000 7.5278 8.8312 0.0000
0.0000 0.0000 7.6752 0.0000 5.5011 7.1961
0.0000 5.6637 NaN 0.0000 0.0000 0.0000
0.0000 6.5408 7.1609 0.0000 8.3373 7.2224
0.0000 7.3378 8.3331 0.0000 7.3221 0.0000
Complex Conditions
You can combine multiple conditions using logical operators. This allows for precise data “clipping” or windowing.
* Use & for AND
* Use | for OR
Matrix A = Rand(5, 6);
A *= 10;
// Set values within the range (5, 8) to a new value
A[(A > 5) & (A < 8)] = 6.5;
Console.WriteLine(A);
Ouput
4.3522 0.3577 6.5000 9.6065 8.5499 4.3567
2.6798 6.5000 3.6096 2.5198 6.5000 2.6803
6.5000 4.0221 6.5000 0.0462 3.6443 9.5927
0.4729 9.7893 1.5422 8.3177 6.5000 6.5000
3.4186 8.7653 6.5000 1.9461 8.6309 1.3130
Advantages
|
|
|---|---|
|
|
|
|
|
|
Example: Finding Integers in a Double Matrix As discussed in the type-checking guidelines, you can use logical indexing to identify and manipulate whole numbers stored as doubles:
Matrix A = new double[,]
{
{1.1, 2.0, 3.9, 4.2 },
{1.5, 3.5, 4.0, 5.1 }
};
Console.WriteLine(A);
// Find all "integers" and scale them by 10
A[A % 1 == 0] *= 10;
Console.WriteLine(A);
Ouput
1.1000 2.0000 3.9000 4.2000
1.5000 3.5000 4.0000 5.1000
1.1000 20.0000 3.9000 4.2000
1.5000 3.5000 40.0000 5.1000