<jupyter_start><jupyter_text>Linear Algebra for ChELaboratory 6: Matrix Operations Discussion<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline

A = np.array ([
    [1,-2,5],
    [6,2,4]
])
A
AT1 = np.transpose(A)
AT1
AT2 = A.T
AT2
np.array_equiv(AT1,AT2)
B = np.array([
      [1,7,3],
      [4,2,6]
])
B.shape
np.transpose(B).shape
B.T.shape<jupyter_output><empty_output><jupyter_text>Dot Product<jupyter_code>X = np.array([
    [1,2],
    [8,3],
    [2,4]
])
Y = np.array([
    [-9,2,1],
    [0,4,2]
])
np.dot(X,Y)
X.dot(Y)
X @ Y
np.matmul(X,Y)<jupyter_output><empty_output><jupyter_text>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 another matrix $B$, then matrix $B$ should have a shape of $(b,c)$ where $b$ and $c$ are any integers. So for given the following matrices:$$A = \begin{bmatrix}4&4\\7&0\\0&1\end{bmatrix}, B = \begin{bmatrix}10&3\\4&31\\0&4\end{bmatrix}, C = \begin{bmatrix}0&1&1\\1&1&2\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 \cdot C$, or $B \cdot C$.<jupyter_code>A = np.array([
    [4, 4],
    [2, 8]
])
B = np.array([
    [13,2,5],
    [3,13,5],
    [-1,-2,5]
])
C = np.array([
    [0,7],
    [1,2],
    [3,6]
])
D = np.array([
    [3,2],
    [5,3],
    [8,9]

])
print(A.shape)
print(B.shape)
print(C.shape)
print(D.shape)
B @ C
D@A
A @ C.T
C @ D.T<jupyter_output><empty_output><jupyter_text>Rule 2: Dot Product has special propertiesDot 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$<jupyter_code>A = np.array([
    [1,6,2],
    [9,5,3],
    [1,4,2]
])
B = np.array([
    [0,9,4],
    [-4,5,-5],
    [-2,6,3]
])
C = np.array([
    [0,0,3],
    [1,4,7],
    [8,3,2]
])
Z = np.zeros(A.shape)
Z
A@Z
np.array_equal(A@Z, Z)
A@B
B@A
np.array_equiv(A@B, B@A)
A@(B@C)
(A@B)@C
np.array_equiv(A@(B@C),(A@B)@C)
A@(B+C)
A@B+A@C
np.array_equiv(A@(B+C),A@B+A@C)
(B+C)@A
B@A+C@A
np.array_equiv((B+C)@A,B@A+C@A)
I = np.eye(3)
I
A@I
A
np.array_equiv(A@I, A)<jupyter_output><empty_output><jupyter_text>Determinants 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 laborat[link text](https://)ory 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 $(2,2)$? 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()`.<jupyter_code>G = np.array([
    [6,7],
    [3,0]
])
np.linalg.det(G)
H = np.array([
              [3,8,1],
              [0,4,2],
              [7,3,7]
])
np.linalg.det(H)
I = np.array([
              [3,8,1,4],
              [0,4,2,7],
              [7,3,7,-9],
              [1,4,6,-3]
])
np.linalg.det(I)<jupyter_output><empty_output><jupyter_text>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$:$$M = \begin{bmatrix}1&7\\-3&5\end{bmatrix}$$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} = \frac{1}{|M|} \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()`.<jupyter_code>M = np.array([
    [4,3],
    [2, -9],
])

np.array(M @ np.linalg.inv(M), dtype=int)
P = np.array([
              [5,8,3],
              [-5,-3,-5],
              [6,2,-7]
])
Q = np.linalg.inv(P)
Q
Q@P<jupyter_output><empty_output><jupyter_text>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 . 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.<jupyter_code>S = np.array([
    [7,3,5,0],
    [0,2,4,8],
    [-4,6,-3,7],
    [11,7,3,5]
])
T= np.array([
    [4,-2,5,5],
    [2,4,1,3],
    [0,9,3,0],
    [8,9,6,8]
])
U = np.array([
    [2,2,1,4],
    [8,9,4,6],
    [0,9,0,2],
    [-7,-9,5,8]
])
X = np.zeros(S.shape)
X
S@X
np.array_equal(S@X, X)
S@T
T@S
np.array_equiv(S@T, T@S)
S@(T@U)
(S@T)@U
np.array_equiv(S@(T@U),(S@T)@U)
S@(T+U)
S@T+S@U<jupyter_output><empty_output><jupyter_text><jupyter_code>np.array_equiv(S@(T+U),S@T+S@U)
(T+U)@S
T@S+U@S
np.array_equiv((T+U)@S,T@S+U@S)
L = np.eye(4)
L
S@L
S
np.array_equiv(S@L, S)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.1751370573418284, 'fraction_numerical': 0.04119128759816269, 'mean_word_length': 3.8076923076923075, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 33, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '568334', 'license': 'Apache-2.0', 'n_tokens_mistral': 2762, 'n_tokens_neox': 2667, 'n_words': 793, 'path': 'Laboratory_6_Matrix_Operations.ipynb', 'repo_name': 'beafrnndz/Linear-Algebra_2ndSemester', 'size': '6749'}