# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

# + [markdown] id="eZJNofDl7AQa"
# # Linear algebra with Numpy
#
# It is possible to do symbolic linear algebrea with [Sympy](http://www.sympy.org/en/index.html) but for numeric computations [Numpy](http://www.numpy.org/) is a high performance library that should be used. 
#
# Here is how it is described: 
#
# > NumPy is the fundamental package for scientific computing with Python. It contains among other things: [...]
#  useful linear algebra, Fourier transform, and random number capabilities.
#
# In this section we will see how to:
#
# - Manipulate matrices;
# - Solve Matrix equations;
# - Calculate Matrix inverse and determinants.
#
# ## Manipulating matrices
#
# It is straightforward to create a Matrix using Numpy. Let us consider the following as a examples:
#
# $$
# A = \begin{pmatrix}
# 5 & 6 & 2\\
# 4 & 7 & 19\\
# 0 & 3 & 12
# \end{pmatrix}
# $$
#
# $$
# B = \begin{pmatrix}
# 14 & -2 & 12\\
# 4 & 4 & 5\\
# 5 & 5 & 1
# \end{pmatrix}
# $$
#
#
# First, similarly to Sympy, we need to import Numpy:

# + id="GQhKbSN87AQe"
import numpy as np

# + [markdown] id="Nifn1UO57AQf"
# Now we can define $A$:

# + id="zuimpJ6b7AQg"
A = np.matrix([[5, 6, 2],
               [4, 7, 19],
               [0, 3, 12]])

# + id="5QQgqYXJ7AQg" outputId="ebbc7982-cc48-4108-9270-28f9bea337a9"
A

# + id="aB4l_iGE7AQh"
B = np.matrix([[14, -2, 12],
               [4, 4, 5],
               [5, 5, 1]])

# + id="WmyseB6t7AQi" outputId="5f80e8fe-f711-4d28-e254-f2ead9bcaa80"
B

# + [markdown] id="SlcVwByY7AQk"
# We can obtain the following straightforwardly:
#
# - $5A$ (or any other scalar multiple of $A$);
# - $A ^ 3$ (or any other exponent of $A$);
# - $A + B$;
# - $A - B$;
# - $AB$

# + id="DPrSOEZD7AQk" outputId="0146e670-18ae-402d-ee6f-44c7a6c9efac"
5 * A

# + id="dVs7RyTp7AQl" outputId="897c626e-ea5f-4a33-f4e3-eb6efbcc922e"
A ** 3

# + id="gtLfsgtU7AQm" outputId="aa4effd9-f507-4098-f643-b91984024040"
A + B

# + id="boAaa6aO7AQm" outputId="dee5590f-4f53-4345-c3e9-4f932d479917"
A - B

# + id="NZG0GfNt7AQn" outputId="56592273-bc04-45b7-eaa8-28422b6e187e"
A * B

# + [markdown] id="abvow2Ph7AQn"
# ---
#
# **EXERCISE** Compute $A ^ 2 - 2 A + 3$ with:
#
# $$A = 
# \begin{pmatrix}
# 1 & -1\\
# 2 & 1
# \end{pmatrix}
# $$
#
# ---
#
# ## Solving Matrix equations
#
# We can use Numpy to (efficiently) solve large systems of equations of the form:
#
# $$Ax=b$$
#
# Let us illustrate that with:
#
# $$
# A = \begin{pmatrix}
# 5 & 6 & 2\\
# 4 & 7 & 19\\
# 0 & 3 & 12
# \end{pmatrix}
# $$
#
# $$
# b = \begin{pmatrix}
# -1\\
# 2\\
# 1 
# \end{pmatrix}
# $$

# + id="NWXwK-2d7AQn"
A = np.matrix([[5, 6, 2],
               [4, 7, 19],
               [0, 3, 12]])
b = np.matrix([[-1], [2], [1]])

# + [markdown] id="NYQeKatS7AQo"
# We use the `linalg.solve` command:

# + id="ig5R2k8J7AQo" outputId="3f03fe40-64d1-4d27-989a-a40a4cca455a"
x = np.linalg.solve(A, b)
x

# + [markdown] id="7lf_2aG87AQo"
# We can verify our result:

# + id="9xcLtGN37AQp" outputId="34fa65cc-d265-48c9-d546-7c2e397a81d1"
A * x

# + [markdown] id="vy_OCnku7AQp"
# ---
#
# **EXERCISE** Compute the solutions to the matrix equation $Bx=b$ (using the $B$ defined earlier).
#
# ---
#
# ## Matrix inversion and determinants
#
# Computing the inverse of a matrix is straightforward:

# + id="zXy55Yn67AQp" outputId="716f43b5-2cf8-4a4b-cb4a-3c143bef482f"
Ainv = np.linalg.inv(A)
Ainv

# + [markdown] id="WAgETXm67AQq"
# We can verify that $A^{-1}A=\mathbb{1}$:

# + tags=["nbval-ignore-output"] id="adT3N8vJ7AQq" outputId="aae85cd6-accb-4a15-ee0d-a8bd1d68a863"
A * Ainv

# + [markdown] id="JlWzfFow7AQq"
# The above might not look like the identity matrix but if you look closer you see that the diagonals are all `1` and the off diagonals are a **very** small number (which from a computer's point of view is `0`).

# + [markdown] id="W6s7DQQj7AQq"
# To calculate the determinant:

# + tags=["nbval-ignore-output"] id="rEBtiMca7AQr" outputId="33929074-9275-47a5-97dd-5dc0a90d73c3"
np.linalg.det(A)

# + id="q-LeceuQ7AQr"


# + id="yhXtL4FA7AQr"


StarcoderdataJupyterScriptsDedupFiltered