# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: percent
#       format_version: '1.3'
#       jupytext_version: 1.14.0
#   kernelspec:
#     display_name: Python [conda env:CompWS22] *
#     language: python
#     name: conda-env-CompWS22-py
# ---

# %% [markdown]
# # Methoden aus numpy.linalg zur Lösung grundlegender Aufgabenstellungen der linearen Algebra
#
# Das linalg Paket aus NumPy stellt Methoden für Aufgabestellung der Linearen Algebra zur Verfügung. Hier ist eine Auswahl. Die Verfahren, die wir in dieser und den nächsten Vorlesungen entwickeln, gibt es meist schon in linalg.
#
# Zuerst muss das "Lineare Algebra"-Modul von numpy importiert werden.

# %%
import numpy as np
import numpy.linalg as la  # numpy linalg modul importieren

# %% [markdown]
#  ## Lösung linearer Gleichungssysteme
#

# %%
A = np.array([[1, 2, 3], [0, 3, 7], [1, 1, 1]])
b = np.array([14, 28, 6])

# löse LGS Ax=b nach x
x = la.solve(A, b)

# %%
A@x-b

# %% [markdown]
#  Man kann auch die Inverse von A berechnen und "x" so berechnen
#  (das wird NICHT empfohlen)

# %%
Ainv = la.inv(A)
x2 = Ainv@b
np.allclose(x, x2)
# ' Wie man sieht, sind die Ergebnisse (fast) gleich.

# %% [markdown]
# ## Determinate

# %%
la.det(A)

# %% [markdown]
# ## Berechne Eigenwerte und Eigenvektoren

# %% [markdown]
# Die Methode eigvals() berechnet Eigenwerte. Für hermitische/symmetrische Matrizen gibt es eigvalsh()

# %%
la.eigvals(A)

# %% [markdown]
# Die Methode eig() berechnet Eigenwerte und und zusätzlich rechte Eigenvektoren. 
# Für hermitische/symmetrische Matrizen gibt es eigh()

# %%
ew, ev = la.eig(A)         

# %%
ew

# %%
ev

# %% [markdown]
# In dem NumPy Array __ev__ sind spaltenweise die Eigenvektoren abgespeichert.
# Wir überprüfen das Ergebnis indem wir die Gleichung $A v - \lambda v$ spaltenweise betrachten.

# %%
A@ev - ev@np.diag(ew)

# %%
np.allclose(A@ev - ev@np.diag(ew), 0)

# %% [markdown]
# ## Norm eines Vektors
# Berechnung der Euklidischen Norm eines Vektors $x \in \mathbb{C}^n$:
# $$
# \| x \| = \sqrt{\sum_{i=1}^n |x_i|^2}
# $$

# %%
la.norm(b)

# %%
np.sqrt(sum(b**2))

# %%
[la.norm(ev[:, j]) for j in range(ev.shape[0])] # Die Eigenvektoren aus eig sind offenbar normiert

# %%
# la.norm kann auch andere Vektor- und Matrixnormen berechnen.
# np.info(la.norm)

# %% [markdown]
# ## Wiederholung numpy
# Operationen auf Arrays/Matrizen

# %%

A = np.array([[10, -7, 0],
              [-3, 2, 6],
              [5, -100, 5]])
A
# %%
# ' erste Zeile von A (Zeile 0)
A[0]
# %%
# ' Zeilentausch
# ' $A[[i, j]] = A[[j, i]]$ vertauscht Zeile i  mit Zeile j

A[[1, 2]] = A[[2, 1]]
A
# %%
# ' x *= y ist eine Kurzschreibweise für x = x * y. Das gibt es auch für +, -, /

i = 3
i *= 2
i

# %%
# ' Addiere das k-Fache der Zeile 1 zur  Zeile 2
k = 2
A[2] += k*A[1]
A

# %%
# ' Achtung
A[2] += 1.2*A[1]

# %%
# ' Fehlerbehebung

A = A.astype(float)
A[2] += 1.2*A[1]
A

# ' oder von Anfang an als float
A = np.array([[10, -7, 0],
              [-3, 2, 6],
              [5, -100, 5]], dtype='float')
A

# %%
