# NumPy#

This notebook covers the basics of NumPy, the Python matrix library.

# NumPy namespace convention: np
import numpy as np

# Create an all-zero matrix
#   NOTE: argument is a tuple '(3, 4)'
#     WRONG: np.zeros(3, 4)
#     CORRECT: np.zeros( (3, 4) )
A = np.zeros((3, 4))

print(A)
print(A.shape)  # dimensions of A

[[0. 0. 0. 0.]
[0. 0. 0. 0.]
[0. 0. 0. 0.]]
(3, 4)

# All-one matrix
B = np.ones((3, 4))
print(B)

# Identity matrix
I = np.eye(5)
print(I)

# Stacking matrices horizontally
#   Use vstack to stack vertically
J = np.hstack((I, I))
print(J)

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

# Random matrix with standard Gaussian entries
#   NOTE: argument is NOT a tuple
Q = np.random.randn(4, 4)

print(Q)
print(Q[:, 1])  # Second column (everything is 0-indexed)
print(Q[2, 3])  # (3, 4) entry (as a real number)

[[-0.60484877 -0.63975676 -0.09774417 -0.0706336 ]
[-0.60055582  0.35880877 -0.89249724 -0.32289222]
[-0.74746465  0.75941857 -0.88504798  0.13107029]
[-1.85710418  0.14337679  0.29059144  1.84972641]]
[-0.63975676  0.35880877  0.75941857  0.14337679]
0.1310702857398909

# Random column vector of length 4
v = np.random.randn(4, 1)

# v.T: v tranpose
# @: matrix multiplication
z = v.T @ Q @ v

# The result is a 1-by-1 matrix
print(z)

# Extract the result as a real number
print(z[0, 0])

[[3.31482169]]
3.3148216886641637

# Other useful methods
#   Construct a matrix
A = np.array([[1, 2], [3, 4]])
B = np.array([[-1, 3.2], [5, 8]])
#   Transpose a matrix
print(A.T)
#   Elementwise multiplication
print(np.multiply(A, B))
#   Sum of each column (as a row vector)
print(np.sum(A, axis=0))
#   Sum of each row (as a column vector)
print(np.sum(A, axis=1))

[[1 3]
[2 4]]
[[-1.   6.4]
[15.  32. ]]
[4 6]
[3 7]

# Linear algebra routines
Q = A.T @ A
(d, V) = np.linalg.eig(Q)  # Eigen decomposition
print("d = ", d)
print("V = ", V)

v = np.array([1, 2])
print("||v||_2 = ", np.linalg.norm(v))  # 2-norm of a vector

Qinv = np.linalg.inv(Q)  # Matrix inverse
# Solves Qx = v (faster than Qinv*v)
x = np.linalg.solve(Q, v)
print("Q^{-1}v = ", x)

d =  [ 0.13393125 29.86606875]
V =  [[-0.81741556 -0.57604844]
[ 0.57604844 -0.81741556]]
||v||_2 =  2.23606797749979
Q^{-1}v =  [-2.   1.5]