<a href="https://colab.research.google.com/github/Astraplas/LinearAlgebra_2ndSem/blob/main/Assignment_5.ipynb" target="_parent"></a>

# Linear Algebra for CHE
## Laboratory 6 : Matrix Operations

Now that you have a fundamental knowledge about representing and operating with vectors as well as the fundamentals of matrices, we'll try to the same operations with matrices and even more.

## Objectives
At the end of this activity you will be able to:

1. Be familiar with the fundamental matrix operations.
2. Apply the operations to solve intemrediate equations.
3. Apply matrix algebra in engineering solutions.

# Discussion

The codes below will serve as the foundation to be used in the matrix operation codes to be created.


```python
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
```

# Transposition

 


One of the fundamental operations in matrix algebra is Transposition. The transpose of a matrix is done by flipping the values of its elements over its diagonals. With this, the rows and columns from the original matrix will be switched. So for a matrix $A$ its transpose is denoted as $A^T$. So for example:


:$$
A=\begin{bmatrix} 1 & 2 & 5 \\ 5 & -1 & 0 \\ 0 & -3 & 3\end{bmatrix} \\
A^T=\begin{bmatrix} 1 & 5 & 0\\ 2 & -1 & -3\\ 5 & 0 & 3\end{bmatrix}
$$

his can now be achieved programmatically by using np.transpose() or using the T method.


```python
A = np.array([
    [1 ,2, 5],
    [5, -1, 0],
    [0, -3, 3]
])
A
```




    array([[ 1,  2,  5],
           [ 5, -1,  0],
           [ 0, -3,  3]])




```python
AT1 = np.transpose(A)
AT1
```




    array([[ 1,  5,  0],
           [ 2, -1, -3],
           [ 5,  0,  3]])




```python
AT2 = A.T
```


```python
np.array_equiv(AT1, AT2)
```




    True




```python
B = np.array([
    [40,13,55,32],
    [32,12,64,21],
])
B.shape
```




    (2, 4)




```python
np.transpose(B).shape
```




    (4, 2)




```python
B.T.shape
```




    (4, 2)



The codes mentioned above have simply interconverted the rows and columns of the given matrices

##### Try to create your own matrix (you can try non-squares) to test transposition.


```python

```

## Dot Product / Inner Product

If you recall the dot product from laboratory activity before, we will try to implement the same operation with matrices. In matrix dot product we are going to get the sum of products of the vectors by row-column pairs. So if we have two matrices $X$ and $Y$:

$$X=\begin{bmatrix}
x_{(0,0)}& x_{(0,1)}\\
x_{(1,0)}& x_{(1,1)}\\
\end{bmatrix}
,
Y=\begin{bmatrix}
y_{(0,0)}& y_{(0,1)}\\
y_{(1,0)}& y_{(1,1)}\\
\end{bmatrix}
$$


The dot product will then be computed as:

$$X=\begin{bmatrix}
x_{(0,0)}*y_{(0,0)} + x_{(0,1)}*y_{(1,0)} & x_{(0,0)}*y_{(0,1)} + x_{(0,1)}*y_{(1,1)}\\
x_{(1,0)}*y_{(0,0)} + x_{(1,1)}*y_{(1,0)} & x_{(1,0)}*y_{(0,1)} + x_{(1,1)}*y_{(1,1)}\\
\end{bmatrix}
$$


So if we assign values to $X$ and $Y$:


:$$
X=\begin{bmatrix} 1 & 2\\ 0 & 1\end{bmatrix}
,
Y=\begin{bmatrix} -1 & 0\\ 2 & 2\end{bmatrix}
$$

:$$
X⋅Y=\begin{bmatrix} 1*-1 + 2*2 & 1*0 + 2*2\\ 0*-1 + 1*2 & 0*0 + 1*2\end{bmatrix} =
\begin{bmatrix} 3 & 4\\ 2 & 2\end{bmatrix}
$$

This could be achieved programmatically using np.dot(), np.matmul() or the @ operator.


```python
X = np.array([
    [3,4,6],
    [1,6,7],
    [6,3,0]
])
Y = np.array([
    [4,-2,5],
    [5,7,-4],
    [5,1,-8]
])
```


```python
np.dot(X,Y)
```




    array([[ 62,  28, -49],
           [ 69,  47, -75],
           [ 39,   9,  18]])




```python
X.dot(Y)
```




    array([[ 62,  28, -49],
           [ 69,  47, -75],
           [ 39,   9,  18]])




```python
X @ Y
```




    array([[ 62,  28, -49],
           [ 69,  47, -75],
           [ 39,   9,  18]])




```python
np.matmul(X,Y)
```




    array([[ 62,  28, -49],
           [ 69,  47, -75],
           [ 39,   9,  18]])



In matrix dot products there are additional rules compared with vector dot products. Since vector dot products were just in one dimension there are less restrictions. Since now we are dealing with Rank 2 vectors we need to consider some rules:

## Rule 1: The inner dimensions of the two matrices in question must be the same.

So given a matrix $A$ with a shape of $(a,b)$ where $a$ and $b$ are any integers. If we want to do a dot product between $A$ and $B$ another matrix , then matrix $B$ should have a shape of $(b,c)$ where $b$ and $c$ are any integers. So for given the following matrices:

$$
Q=\begin{bmatrix} 5 & 5 & 5\\ 6 & 7 & -2\end{bmatrix},
R=\begin{bmatrix} 4 & 5 & 6\\ 9 & 0 & 1\end{bmatrix},
S=\begin{bmatrix} 1 & 6\\ 3 & 2\\ 4 & 6\end{bmatrix}
$$

So in this case $A$ has a shape of $(3,2)$, $B$ has a shape of $(3,2)$ and $C$ has a shape of $(2,3)$ . So the only matrix pairs that is eligible to perform dot product is matrices $A⋅C$ or $B⋅C$.


```python
Q = np.array([
    [5, 5, 5],
    [6, 7, -2]
])
R = np.array([
    [4,5,6],
    [9,0,1]
])
S = np.array([
    [1,6],
    [3,2],
    [4,6]
])
print(Q.shape)
print(R.shape)
print(S.shape)

```

    (2, 3)
    (2, 3)
    (3, 2)



```python
Q @ S
```




    array([[40, 70],
           [19, 38]])




```python
R @ S
```




    array([[43, 70],
           [13, 60]])



If you would notice the shape of the dot product changed and its shape is not the same as any of the matrices we used. The shape of a dot product is actually derived from the shapes of the matrices used. So recall matrix $A$ with a shape of $(a,b)$ and matrix $B$ with a shape of $(b,c)$, $A⋅B$ should have a shape $(a,c)$.


```python
Q @ R.T
```




    array([[75, 50],
           [47, 52]])




```python
X = np.array([
    [1,2,3,0]
])
Y = np.array([
    [1,0,4,-1]
])
print(X.shape)
print(Y.shape)
```

    (1, 4)
    (1, 4)



```python
R.T @ Q
```




    array([[74, 83,  2],
           [25, 25, 25],
           [36, 37, 28]])



And you can see that when you try to multiply A and B, it returns ValueError pertaining to matrix shape mismatch.

## Rule 2: Dot Product has special properties

Dot products are prevalent in matrix algebra, this implies that it has several unique properties and it should be considered when formulation solutions:

1. $A \cdot B \neq B \cdot A$
2. $A \cdot (B \cdot C) = (A \cdot B) \cdot C$
3. $A\cdot(B+C) = A\cdot B + A\cdot C$
4. $(B+C)\cdot A = B\cdot A + C\cdot A$
5. $A\cdot I = A$
6. $A\cdot \emptyset = \emptyset$


I'll be doing just one of the properties and I'll leave the rest to test your skills!


```python
H = np.array([
    [5,6,11],
    [7,4,10],
    [8,7,0]
])
I = np.array([
    [7,18,26],
    [44,31,0],
    [5,5,6]
])
J = np.array([
    [3,0,1],
    [6,0,2],
    [7,8,8]
])
```


```python
H.dot(np.zeros(H.shape))
```




    array([[0., 0., 0.],
           [0., 0., 0.],
           [0., 0., 0.]])




```python
l_mat = np.zeros(H.shape)
l_mat
```




    array([[0., 0., 0.],
           [0., 0., 0.],
           [0., 0., 0.]])




```python
r_dot_l = H.dot(np.zeros(H.shape))
r_dot_l
```




    array([[0., 0., 0.],
           [0., 0., 0.],
           [0., 0., 0.]])




```python
np.array_equal(r_dot_l,l_mat)
```




    True




```python
null_mat = np.empty(H.shape, dtype=float)
null = np.array(null_mat,dtype=float)
print(null)
np.allclose(r_dot_l,null)
```

    [[0. 0. 0.]
     [0. 0. 0.]
     [0. 0. 0.]]





    True




```python
K = H.dot(I)
L = I.dot(H)
print(K)
print(L)
  
row1 = len(K);  
col1 = len(K[0]);  
 
row2 = len(L);  
col2 = len(L[0]);  
  
if(row1 != row2 or col1 != col2):  
    print("Matrices are not equal"); 
else:  
    for i in range(0, row1):  
        for j in range(0, col1):  
            if(K[i][j] != L[i][j]):  
                flag = False;  
                break;  
      
    if(flag):  
        print("Matrices are equal");  
    else:  
        print("Matrices are not equal. Therefore A⋅B ≠ B⋅A"); 
```

    [[354 331 196]
     [275 300 242]
     [364 361 208]]
    [[369 296 257]
     [437 388 794]
     [108  92 105]]
    Matrices are not equal. Therefore A⋅B ≠ B⋅A



```python
v = print(H*(I*J))
w = print((H*I)*J)

if v == w:
  print("Thus, A⋅(B⋅C)=(A⋅B)⋅C")
```

    [[ 105    0  286]
     [1848    0    0]
     [ 280  280    0]]
    [[ 105    0  286]
     [1848    0    0]
     [ 280  280    0]]
    Thus, A⋅(B⋅C)=(A⋅B)⋅C



```python
a = print(H*(I+J))
b = print(H*I+H*J)
if a == b:
  print("Thus, A⋅(B+C)=A⋅B+A⋅C")
```

    [[ 50 108 297]
     [350 124  20]
     [ 96  91   0]]
    [[ 50 108 297]
     [350 124  20]
     [ 96  91   0]]
    Thus, A⋅(B+C)=A⋅B+A⋅C



```python
c = print((I+J)*H)
d= print(I*H+J*H)
if c == d:
  print("Thus, (B+C)⋅A=B⋅A+C⋅A")
```

    [[ 50 108 297]
     [350 124  20]
     [ 96  91   0]]
    [[ 50 108 297]
     [350 124  20]
     [ 96  91   0]]
    Thus, (B+C)⋅A=B⋅A+C⋅A



```python
H.dot(1)
```




    array([[ 5,  6, 11],
           [ 7,  4, 10],
           [ 8,  7,  0]])




```python
H.dot(0)
```




    array([[0, 0, 0],
           [0, 0, 0],
           [0, 0, 0]])



The codes mentioned above proves that the rule established for the special properties of the dot products are true.

## Determinant

A determinant is a scalar value derived from a square matrix. The determinant is a fundamental and important value used in matrix algebra. Although it will not be evident in this laboratory on how it can be used practically, but it will be reatly used in future lessons.

The determinant of some matrix $A$ is denoted as $det(A)$ or $|A|$. So let's say $A$ is represented as:

$$A=\begin{bmatrix}
a_{(0,0)}& a_{(0,1)}\\
a_{(1,0)}& a_{(1,1)}\\
\end{bmatrix}
$$


We can compute for the determinant as:

$$
|A| = a_{(0,0)} * a_{(1,1)} - a_{(1,0)} * a_{(0,1)}
$$


So if we have $A$ as:

$$A=\begin{bmatrix}
1 & 4\\
0 & 3\\
\end{bmatrix},
|A| = 3
$$


But you might wonder how about square matrices beyond the shape ? We can approach this problem by using several methods such as co-factor expansion and the minors method. This can be taught in the lecture of the laboratory but we can achieve the strenuous computation of high-dimensional matrices programmatically using Python. We can achieve this by using np.linalg.det().


```python

```


```python
A = np.array([
    [58,32],
    [71,36]
])
np.linalg.det(A)
```




    -184.00000000000006




```python
## Now other mathematics classes would require you to solve this by hand, 
## and that is great for practicing your memorization and coordination skills 
## but in this class we aim for simplicity and speed so we'll use programming
## but it's completely fine if you want to try to solve this one by hand.
B = np.array([
    [54,321,53],
    [90,13,10],
    [34,192,81]
])
np.linalg.det(B)
```




    -1385353.9999999995



## Inverse

The inverse of a matrix is another fundamental operation in matrix algebra. Determining the inverse of a matrix let us determine if its solvability and its characteristic as a system of linear equation — we'll expand on this in the nect module. Another use of the inverse matrix is solving the problem of divisibility between matrices. Although element-wise division exists but dividing the entire concept of matrices does not exists. Inverse matrices provides a related operation that could have the same concept of "dividing" matrices.

Now to determine the inverse of a matrix we need to perform several steps. So let's say we have a matrix $M$:

$$A=\begin{bmatrix}
1 & 7\\
-3 & 5\\
\end{bmatrix},
|A| = 3
$$


First, we need to get the determinant of $M$.

$$
|M| = (1)(5) - (-3)(7) = 26
$$


Next, we need to reform the matrix into the inverse form

$$
M^{-1} = \begin{align} \frac{1}{|M|} \end{align} 
\begin{bmatrix}
m_{(1,1)}& -m_{(0,1)}\\
-m_{(1,0)}& m_{(0,0)}\\
\end{bmatrix}
$$

So that will be:

$$M^{-1} = \frac{1}{26} \begin{bmatrix} 5 & -7 \\ 3 & 1\end{bmatrix} = \begin{bmatrix} \frac{5}{26} & \frac{-7}{26} \\ \frac{3}{26} & \frac{1}{26}\end{bmatrix}$$

For higher-dimension matrices you might need to use co-factors, minors, adjugates, and other reduction techinques. To solve this programmatially we can use np.linalg.inv().


```python
M = np.array([
    [17,57],
    [23, 45]
])

np.array(M @ np.linalg.inv(M), dtype=int)
```




    array([[0, 0],
           [0, 0]])




```python
## And now let's test your skills in solving a matrix with high dimensions:
N = np.array([
    [12,45,423,121,10,533,553],
    [560,255,34,513,524,242,23],
    [55,49,220,235,205,10,356],
    [61,56,24,24,38,443,15],
    [86,426,283,724,12,624,241],
    [-5,-165,222,230,-310,356,330],
    [224,521,132,246,146,-225,132],
])
N_inv = np.linalg.inv(N)
np.array(N @ N_inv,dtype=int)
```




    array([[0, 0, 0, 0, 0, 0, 0],
           [0, 1, 0, 0, 0, 0, 0],
           [0, 0, 1, 0, 0, 0, 0],
           [0, 0, 0, 0, 0, 0, 0],
           [0, 0, 0, 0, 1, 0, 0],
           [0, 0, 0, 0, 0, 1, 0],
           [0, 0, 0, 0, 0, 0, 1]])



To validate the wether if the matric that you have solved is really the inverse, we follow this dot product property for a matrix $M$:

$$
M⋅M^{-1} = I
$$


```python
squad = np.array([
    [1.0, 1.0, 0.5],
    [0.7, 0.7, 0.9],
    [0.3, 0.3, 1.0]
])
weights = np.array([
    [0.2, 0.2, 0.6]
])
p_grade = squad @ weights.T
p_grade
```




    array([[0.7 ],
           [0.82],
           [0.72]])



## Activity

### Task 1

Prove and implement the remaining 6 matrix multiplication properties. You may create your own matrices in which their shapes should not be lower than $(3,3)$. In your methodology, create individual flowcharts for each property and discuss the property you would then present your proofs or validity of your implementation in the results section by comparing your result to present functions from NumPy.

## Conclusion

For your conclusion synthesize the concept and application of the laboratory. Briefly discuss what you have learned and achieved in this activity. Also answer the question: "how can matrix operations solve problems in healthcare?".

Conclusion in the Lab report:

In light of the learnings that the students were able to obtain, it could also be applied to several fields. One of which is that the matrix operation could be used through the healthcare system. In the healthcare system, there is a field that is specialized in treating certain eye illnesses. In this particular field, the use of optics would be prevalent in order to execute a certain diagnosis. With the help of matrices, calculating the reflection and refraction of a certain light would be comparatively easy compared to calculating it through the manual method. Furthermore, it could also be applied when dealing with certain bills that are needed to be paid for healthcare. In here, rectangular arrays of matrices are used by a certain program that would flexibly conduct the calculations quickly and efficiently. With all of this in mind, it is irrefutable that matrix does indeed ease the burden when it comes to solving problems in healthcare. As such, exploring the applications and possibilities of matrices is imperative in order to nurture a better living.

79d3c348bba8081f91f7d7c461ab722c655f1649
ipynb
Jupyter Notebook
Assignment_5.ipynb
Astraplas/LinearAlgebra_2ndSem
6a19a2f2e106e5ba2d995d609d82cde2d0dd83ae
Apache-2.0
Assignment_5.ipynb
Astraplas/LinearAlgebra_2ndSem
6a19a2f2e106e5ba2d995d609d82cde2d0dd83ae
Apache-2.0
Assignment_5.ipynb
Astraplas/LinearAlgebra_2ndSem
6a19a2f2e106e5ba2d995d609d82cde2d0dd83ae
Apache-2.0
Qwen/Qwen-72B
1. YES
2. YES