<jupyter_start><jupyter_text>Linear Algebra for ChE Laboratory 2: Matrices Now that you have a fundamental knowledge about Python, we'll try to look into greater dimensions. Objectives At the end of this activity you will be able to:1. Be familiar with matrices and their relation to linear equations.2. Perform basic matrix operations.3. Program and translate matrix equations and operations using Python.  Discussion<jupyter_code>import numpy as np
## numpy - numerical python
import matplotlib.pyplot as plt
## matrix library
import scipy.linalg as la
%matplotlib inline<jupyter_output><empty_output><jupyter_text>Matrices The notation and use of matrices is probably one of the fundamentals of modern computing. Matrices are also handy representations of complex equations or multiple interrelated equations from 2d equations to even hundreds and thousands of them. Let's say for example you have A and B as system of equation. $$A = \left\{    \begin{array}\        x + y \\         4x - 10y    \end{array}\right. \\B = \left\{    \begin{array}\        x+y+z \\         3x -2y -z \\        -x + 4y +2z    \end{array}\right. \\C = \left\{    \begin{array}\        w -2x + 3y - 4z \\         3w - x - 2y + z\\        2w - x + 3y - 2z    \end{array}\right. \\ $$ We could see that A is a system of 2 equations with 2 parameters. While B is a system of equations with 3 parameters. We can represent them as matrices as: :$$A=\begin{bmatrix} 1 & 1 \\ 4 & {-10}\end{bmatrix} \\B=\begin{bmatrix} 1 & 1 & 1 \\ 3 & -2 & -1 \\ -1 & 4 & 2\end{bmatrix} \\C=\begin{bmatrix} 1 & -2 & 3 & -4 \\ 3 & -1 & -2 & 1 \\ 2 & -1 & 3 & -2\end{bmatrix}$$ So assuming that you already discussed the fundamental representation of matrices, their types, and operations. We'll proceed in doing them in here in Python. Declaring Matrices Just like our previous laboratory activity, we'll represent system of linear equations as a matrix. The entities or numbers in matrices are called the elements of a matrix. These elements are arranged and ordered in rows and columns which form the list/array-like structure of matrices. And just like arrays, these elements are indexed according to their position with respect to their rows and columns. This can be represented just like the equation below. Whereas $A$ is a matrix consisting of elements denoted by $a_{i,j}$. Denoted by $i$ is the number of rows in the matrix while $j$ stands for the number of columns.Do note that the $size$ of a matrix is $i\times j$. $$A=\begin{bmatrix}a_{(0,0)}&a_{(0,1)}&\dots&a_{(0,j-1)}\\a_{(1,0)}&a_{(1,1)}&\dots&a_{(1,j-1)}\\\vdots&\vdots&\ddots&\vdots&\\a_{(i-1,0)}&a_{(i-1,1)}&\dots&a_{(i-1,j-1)}\end{bmatrix}$$ We already gone over some of the types of matrices as vectors but we'll further discuss them in this laboratory activity. Since you already know how to describe vectors using shape, dimensions and size attributes, we'll use them to analyze these matrices.<jupyter_code>## Since we'll keep on describing matrices. Let's make a function.
def describe_mat(matrix):
  print(f'Matrix:\n{matrix}\n\nShape:\t{matrix.shape}\nRank:\t{matrix.ndim}\n')
## Declaring a 2 x 2 matrix
A = np.array([
    [1, 2],
    [3, 1]
])
describe_mat(A)
G = np.array([
    [1, 1, 3],
    [2, 2, 4]          
])
describe_mat(G)
## Declaring a 3 x 2 matrix
B = np.array([
    [8, 2],
    [5, 4],
    [1, 1]          
])
describe_mat(B)
H = np.array([1,8,1,1])
describe_mat(H)<jupyter_output>Matrix:
[1 8 1 1]

Shape:	(4,)
Rank:	1<jupyter_text>Categorizing Matrices There are several ways of classifying matrices. Once could be according to their shape and another is according to their element values. We'll try to go through them. According to shape Row and Column Matrices **Row** and column matrices are common in vector and matrix computations. They can also represent row and column spaces of a bigger vector space. Row and column matrices are represented by a single column or single row. So with that being, the shape of row matrices would be $1 \times j$ and column matrices would be $i \times 1$.<jupyter_code>## Declaring a Row Matrix
rm1D = np.array([
   1, 3, 2, -4                    
]) ## this is a 1-D Matrix with a shape of (3,), it's not really considered as a row matrix.
rm2D = np.array([
  [1, 2, 3, -4]
]) ## This is a 2-D Matrix with a shape of (1, 3)
describe_mat(rm1D)
describe_mat(rm2D)
## Declaring a column matrix

colmat = np.array([
  [1],
  [8],
  [5]                 
]) ## This is a 2-D Matrix with a shape of (3, 1)
describe_mat(colmat)<jupyter_output><empty_output><jupyter_text>Square Matrices Square matrices are that have the same row and column sizes. We could say a matrix is square if i = j. We can tweak our matrix descriptor function to determine square matrices<jupyter_code>def describe_mat(matrix):
  is_square = True if matrix.shape[0] == matrix.shape[1] else False
  print(f'Matrix:\n{matrix}\n\nShape:\t{matrix.shape}\nRank:\t{matrix.ndim}\nIs Square: {is_square}\n')
square_mat = np.array([
  [1, 6, 8],
  [8, 8, 9],
  [10, 6, 8]
])
nsq = np.array([
  [3, 8, 0],
  [0, 8, 10]
])
describe_mat(square_mat)
describe_mat(nsq)<jupyter_output>Matrix:
[[ 1  6  8]
 [ 8  8  9]
 [10  6  8]]

Shape:	(3, 3)
Rank:	2
Is Square: True

Matrix:
[[ 3  8  0]
 [ 0  8 10]]

Shape:	(2, 3)
Rank:	2
Is Square: False<jupyter_text>According to element values  Null Matrix A Null Matrix is a matrix that has no elements. It is always a subspace of any vector or matrix.<jupyter_code>def describe_mat(matrix):
  if matrix.size > 0:
    is_square = True if matrix.shape[0] == matrix.shape[1] else False
    print(f'Matrix:\n{matrix}\n\nShape:\t{matrix.shape}\nRank:\t{matrix.ndim}\nIs Square: {is_square}\n')
  else:
    print('Matrix is null.')
nm = np.array([])
describe_mat(nm)<jupyter_output>Matrix is null.<jupyter_text>Zero Matrix A zero matrix can be any rectangular matrix but with all elements having a value of 0.<jupyter_code>zmr = np.zeros((1,2))
zms = np.zeros((2,2))
zmrct = np.zeros((3,2))

print(f'Zero Row Matrix: \n{zmr}')
print(f'Zero Square Matrix: \n{zms}')
print(f'Zero Rectangular Matrix: \n{zmrct}')<jupyter_output>Zero Row Matrix: 
[[0. 0.]]
Zero Square Matrix: 
[[0. 0.]
 [0. 0.]]
Zero Rectangular Matrix: 
[[0. 0.]
 [0. 0.]
 [0. 0.]]<jupyter_text>Ones Matrix A ones matrix, just like the zero matrix, can be any rectangular matrix but all of its elements are 1s instead of 0s.<jupyter_code>## Ones Matrix - to get the identity

omr = np.ones((1,2))
oms = np.ones((2,2))
omrct = np.ones((3,2))

print(f'Ones Row Matrix: \n{omr}')
print(f'Ones Square Matrix: \n{oms}')
print(f'Ones Rectangular Matrix: \n{omrct}')<jupyter_output>Ones Row Matrix: 
[[1. 1.]]
Ones Square Matrix: 
[[1. 1.]
 [1. 1.]]
Ones Rectangular Matrix: 
[[1. 1.]
 [1. 1.]
 [1. 1.]]<jupyter_text>Diagonal Matrix A diagonal matrix is a square matrix that has values only at the diagonal of the matrix.<jupyter_code>## Diagonal Matrix
np.array([
  [8, 0, 0],
  [0, 8, 0],
  [0, 0, 8]
])
# a[1,1], a[2,2], a[3,3], ... a[n-1, n-1]
d = np.diag([8, 10, 9, 8])
d<jupyter_output><empty_output><jupyter_text>Identity Matrix An identity matrix is a special diagonal matrix in which the values at the diagonal are ones.<jupyter_code>np.eye(3)
np.identity(5)<jupyter_output><empty_output><jupyter_text>Upper Triangular Matrix<jupyter_code><jupyter_output><empty_output><jupyter_text>An upper triangular matrix is a matrix that has no values below the diagonal.<jupyter_code>np.array([
  [8, 9, 3, 6],
  [0, 10, 1, 4],
  [0, 0, 8, 5],
  [0, 0, 0, 11]        
])<jupyter_output><empty_output><jupyter_text>Lower Triangular Matrix A lower triangular matrix is a matrix that has no values above the diagonal.<jupyter_code>np. array([
  [1, 0, 0, 0],
  [7, 8, 0, 0],
  [10, 9, 8, 0],
  [9, 8, 5, 4]
])<jupyter_output><empty_output><jupyter_text>Practice 1. Given the linear combination below, try to create a corresponding matrix representing it. $$\theta = 5x + 3y - z$$<jupyter_code>theta = np.array([
  [5, 3, -1]
])
theta<jupyter_output><empty_output><jupyter_text>:$$θ =\begin{bmatrix} 5 & 3 & -1\end{bmatrix} \\$$ 2. Given the system of linear combinations below, try to encode it as a matrix. Also describe the matrix. $$A = \left\{\begin{array}5x_1 + 2x_2 +x_3\\4x_2 - x_3\\10x_3\end{array}\right.$$<jupyter_code>import numpy as np
import matplotlib.pyplot as plt
import scipy.linalg as la
%matplotlib inline
def describe_mat(matrix):
    print(f'Matrix:\n{matrix}\n\nShape:\t{matrix.shape}\nRank:\t{matrix.ndim}\n')
A= np.array ([
              [1,2,1],
              [0,4,-3],
              [0,0,10]
])
A
describe_mat(A)<jupyter_output>Matrix:
[[ 1  2  1]
 [ 0  4 -3]
 [ 0  0 10]]

Shape:	(3, 3)
Rank:	2<jupyter_text>The matrix above is a three by three square matrix with rank of 2 and is an upper triangular matrix. 3. Given the matrix below, express it as a linear combination in a markdown and a LaTeX markdown<jupyter_code>G = np.array([
    [1,7,8],
    [2,2,2],
    [4,6,7]
])
G<jupyter_output><empty_output><jupyter_text>:$$G=\begin{bmatrix} 1 & 7 & 8 \\ 2 & 2 & 2 \\ 4 & 6 & 7 \end{bmatrix} $$ $$G = \left\{\begin{array}1x_1 + 7x_2 +8x_3\\2x_1+2x_2 - 2x_3\\4x_1 + 6x_2 + 7x_3\end{array}\right.$$ 4. Given the matrix below, display the output as a LaTeX markdown also express it as a system of linear combinations.<jupyter_code>H = np.tril(G)
H<jupyter_output><empty_output><jupyter_text>:$$H=\begin{bmatrix} 1 & 0 & 0 \\ 2 & 2 & 0 \\ 4 & 6 & 7 \end{bmatrix} $$ $$H = \left\{\begin{array}1x_1  \\2x_1+2x_2 \\4x_1 + 6x_2 + 7x_3\end{array}\right.$$  Matrix Algebra  Addition<jupyter_code>J = np.array([
    [0, 9],
    [0, 8],
    [0, 1]
])
RO = np.array([
    [1, 0],
    [0, 8],
    [0, 2]
])
J+RO
3+J<jupyter_output><empty_output><jupyter_text>Subtraction<jupyter_code>RO - J
5-RO<jupyter_output><empty_output><jupyter_text>Element-wise Multiplication<jupyter_code>J*RO
np.multiply(J,RO)
45*RO
RO@J
alpha=10**-10
J/(alpha+RO)
np.add(RO,J)<jupyter_output><empty_output><jupyter_text>Activity Task 1 Create a function named mat_desc() that througouhly describes a matrix, it should:1. Displays the shape, size, and rank of the matrix.2. Displays whether the matrix is square or non-square.3. Displays whether the matrix is an empty matrix.4. Displays if the matrix is an identity, ones, or zeros matrixUse 3 sample matrices in which their shapes are not lower than (3, 3).In your methodology, create a flowchart discuss the functions and methods you have done. Present your results in the results section showing the description of each matrix you have declared.<jupyter_code>## Function area
import numpy as np
import matplotlib.pyplot as plt
import scipy.linalg as la
%matplotlib inline
def mat_desc (matrix):
  if matrix.size > 0:
    if matrix.shape [0] == matrix.shape[1]:
      s = "Square."
    else:
      s = "Non-square."
    if np.all(matrix == np.identity(matrix.shape[0])):
      sp = "Identity Matrix."
    elif np.all(matrix == np.zeros(matrix.shape)):
      sp = "Zero Matrix."
    elif np.all(matrix == np.ones(matrix.shape)):
      sp = "Ones Matrix."
    else:
      sp = "None."
    print(f'Matrix:\n{matrix}\n\nShape:\t{matrix.shape}\nRank:\t{matrix.ndim}\nSquare?: {s}\nSpecial Characteristics: {sp}') 
  else:
    print('Matrix is Empty')
## Matrix declarations
hi = np.array([
    [3,1,2,4],
    [4,7,9,6],
    [9,1,6,7]
])
one = np.array([
    [1,1,1,1,1],
    [1,1,1,1,1],
    [1,1,1,1,1],
    [1,1,1,1,1],
])
id = np.array([
    [1,0,0,0],
    [0,1,0,0],
    [0,0,1,0],
    [0,0,0,1]
])
## Test Areas
mat_desc(hi)
mat_desc(one)
mat_desc(id)<jupyter_output>Matrix:
[[1 0 0 0]
 [0 1 0 0]
 [0 0 1 0]
 [0 0 0 1]]

Shape:	(4, 4)
Rank:	2
Square?: Square.
Special Characteristics: Identity Matrix.<jupyter_text>Task 2 Create a function named `mat_operations()` that takes in two matrices and input parameters it should: 1. Determine if the matrices are viable for operation and returns your own error message if they are not viable. 2. Returns the sum of the matrices. 3. Returns the difference of the matrices. 4. Returns the element-wise multiplication of the matrices. 5. Returns the element-wise division of the matrices.Use 3 sample matrices in which their shapes are not lower than $(3,3)$.In your methodology, create a flowchart discuss the functions and methods you have done. Present your results in the results section showing the description of each matrix you have declared.<jupyter_code># Function Area
import numpy as np
import matplotlib.pyplot as plt
import scipy.linalg as la
%matplotlib inline
def mat_operations(matA, matB):
  op = input("Enter operation(+,-,*,/): ")
  if matA.shape == matB.shape:
    # proceed to operations
    if op == '+':
      return matA + matB
    elif op == '-':
      return matA - matB
    elif op == '*':
      return matA * matB
    elif op == '/':
      return matA / matB
    else:
      print("The matrices are viable but you have not inputted a correct operation.")
  else:
    print("Huhuhu.The matrices are not viable.")
#matrix declarations
A = np.array([
    [1,1,1,1],
    [1,1,1,1],  
    [1,1,1,1],
    [1,1,1,1]
])
B = np.array([
    [2,2,2,2],
    [2,2,2,2],
    [2,2,2,2],
    [2,2,2,2],
])
C = np.array([
    [3,3,3,3,3],
    [3,3,3,3,3],
    [3,3,3,3,3]
])
D = np.array([
    [4,4,4,4,4],
    [4,4,4,4,4],
    [4,4,4,4,4]
])
mat_operations(A,C)
mat_operations(D, C)
mat_operations(A,B)<jupyter_output>Enter operation(+,-,*,/): -
{'fraction_non_alphanumeric': 0.14101988933752055, 'fraction_numerical': 0.04030207866008673, 'mean_word_length': 3.692982456140351, 'pattern_counts': {'":': 0, '<': 77, '<?xml version=': 0, '>': 79, 'https://': 0, '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': '21', 'dir': 'jupyter-structured-clean-dedup', 'id': '233232', 'license': 'Apache-2.0', 'n_tokens_mistral': 5061, 'n_tokens_neox': 4759, 'n_words': 1642, 'path': 'Laboratory_2_Matrices.ipynb', 'repo_name': 'jgrdn-srn/Linear-Algebra_2ndSem', 'size': '13374'}