### 3.12.Assignment #3: Matrix Properties and Operations(back to full lecture notes)

In this assignment you will use Python to implement several of the vector and matrix operations and predicates we have introduced so far. Please submit a single file a3.py containing your solutions.

Your file may not import any modules or employ any external library functions (unless the problem statement explicitly permits this). You will be graded on the correctness, concision, and mathematical legibility of your code. The different problems and problem parts rely on each other; carefully consider whether you can use functions you define in one part within subsequent parts.

1. In this assignment, we will represent vectors as Python tuples. For example, the vector [1;2;3] would be represented in Python as (1,2,3).

1. Define a Python function scale(s,v) that takes two arguments: a scalar s and a vector v. The function should return the result of multiplying the vector by the scalar.
scale(2, (3,4,5))     # Returns (6,8,10).
2. Define a Python function norm(v) that takes a vector argument v and returns its norm (i.e., its length).

3. Define a Python function dot(v,w) that takes two vectors as arguments and returns their dot product. If the two vectors do not have the same number of components, the function should return None.

Include a comment explaining exactly how many addition operations and how many multiplication operations your implementation performs when the input is a pair of vectors that each have n components.
dot((1,2), (3,4))     # Returns 11.
dot((5,1,0), (1,1,2)) # Returns 6.
dot((5,1,0,4), (1,2)) # Returns None.
4. Define a Python function orthogonal(v,w) that takes two vectors as arguments and returns True only if the two input vectors are orthogonal to one another; if they are not orthogonal, it returns False. If the two vectors do not have the same number of components, the function should return None.

5. Define a Python function proj(v,w) that takes two vectors as arguments and returns a vector that is the projection of v onto w.
2. In this assignment, we will represent matrices as tuples of tuples. For example, the matrix

 1 2 3 4

would be represented as ((1,2),(3,4)):

1. Define a Python function mult(A,B) that takes two matrix arguments and returns the matrix that is the result of multiplying A by B. If either argument is not a valid matrix, or if the two matrices cannot be multiplied because the number of rows and/or columns does not match as necessary, the function should return None.

Include a comment explaining exactly how many addition operations and how many multiplication operations your implementation performs when the input is a pair of matrices that each have n rows and columns.
mult(((1,2),(3,4)), ((-5,-6),(7,8)))     # Returns ((9,10),(13,14)).
2. Define a Python function upper(A) that takes a matrix argument and returns True only if the matrix A is upper triangular; otherwise, it returns False.
3. Define a Python function transpose(A) that takes a matrix argument and returns the transpose of that matrix.
4. Define a Python function lower(A) that takes a matrix argument and returns True only if the matrix A is lower triangular; otherwise, it returns False.
5. Define a Python function symmetric(A) that takes a matrix argument and returns True only if the matrix A is symmetric; otherwise, it returns False.
6. Define a Python function orthogonal(A) that takes a matrix argument and returns True only if the matrix A is orthogonal; otherwise, it returns False. You may name this function orthogonal2() if all your tests are at the end of your file and you don't want this definition to hide the definition of orthogonal() from Problem #1(d) above.
3. In this problem you will implement functions that can be used to generate elementary matrices corresponding to the three types of elementary row operations. In matrices, the first row is indexed as i = 1 (unlike tuples and lists in Python). Thus, you will need to convert the row indices i and j into the corresponding Python tuple indices. Hint: begin with the identity matrix and apply the row operation to that matrix.
1. Define a Python function elementary_swap(r,c,i,j). This function should take four integer arguments:
• r is the number of rows
• c is the number of columns;
• i and j are the two rows to swap.
The function should return a single matrix that can then be used to swap row i with row j by using matrix multiplication. See example below.
mult(elementary_swap(3,3,1,3), ((1,2,3), (0,0,0), (7,8,9))) # Returns ((7,8,9), (0,0,0), (1,2,3)).
2. Define a Python function elementary_scale(r,c,i,s). This function should take four integer arguments:
• r and c are the row and column counts, respectively;
• i is the row to scale;
• s is the scalar to use.
The function should return a single matrix that can then be used to scale row i using the scalar s. See example below.
mult(elementary_scale(2,2,2,-1), ((1,2), (3,4))) # Returns ((1,2), (-3,-4)).
3. Define a Python function elementary_add(r,c,i,j,s). This function should take four integer arguments:
• r and c are the row and column counts, respectively;
• i is the row to add to row j;
• s is the scalar with which to scale row i before adding it to row j.
The function should return a single matrix that can then be used to add a multiple (by s) of row i to row j. See example below.
mult(elementary_add(3,3,1,2,-1), ((1,2,3), (0,0,0), (7,8,9))) # Returns ((1,2,3), (-1,-2,-3), (7,8,9)).
4. Define a Python function is_rref(A) that takes a matrix argument A and returns True only if A is in reduced row echelon form; otherwise, it returns False.