## Des exemples d'utilisation des matrices
import numpy as np
# import matplotlib.pyplot as plt

# Créer une matrice
A = np.array([[1,2,3],[4,5,6],[7,8,9]])
print(f"La matrice A vaut\n{A}\n")

# Créer une matrice 2
list_B = [[1,2,3],
          [4,5,6],
          [7,8,9]]

B = np.array(list_B)
print(f"La matrice B vaut\n{B}\n")

print(f"B.size = {B.size}")
print(f"B.shape = {B.shape}")

I,J=B.shape # Extraction
print(f"I = {I}")
print(f"J = {J}")

print(f"np.zeros(2)={np.zeros(2)}")
print(f"np.zeros([3,5])=\n{np.zeros([3,5])}")

# Opérations

list_B = [[1,2,3],
          [4,5,6],
          [7,8,9]]

def f(x):
    return 2*x+5-x*x

B = np.array(list_B)
# C = np.array(list_B)+1
# C = np.array(list_B)**2
C = f(np.array(list_B))

print(f"B = \n{B}")
print(f"C = \n{C}")


id = np.eye(3) # Définition de l'identité (3 donne matrice 3x3)
print(f"L'identité :\n'{id}")

print(f"id*C = \n{id*C}") # Produit termes à termes
print(f"np.dot(id,C) = \n{np.dot(id,C)}") # Produit matriciel
print(f"id@C = \n{id@C}") # Produit matriciel

print(f"Déterminant de l'identité : {np.linalg.det(id)}")
print(f"Déterminant de B : {np.linalg.det(B)}")

print(f"Inverse de l'identité :\n{np.linalg.inv(id)}")
# print(f"Inverse de B :\n{np.linalg.inv(B)}") # Matrice singulière

# Résolution de systèmes linéaires :
list_A = [[1,2,3],
          [4,5,6],
          [7,8,10]]

A = np.array(list_A)
list_b = [1,2,3]
b = np.array(list_b)

x = np.linalg.solve(A,b)

print(f"Solution de Ax=b :\n{x}")
print(f"Ax : {A@x}")
print(f"b : {b}")

# Librairies sans préfixes
from numpy import *
from numpy.linalg import *

list_A = [[1,2,3],
          [4,5,6],
          [7,8,10]]

A = array(list_A)
print(f"La matrice A vaut\n{A}\n")
list_b = [1,2,3]
b = array(list_b)

x = solve(A,b)

print(f"{A[0,0]}\n") # Premier coef
print(f"{A[0]}\n")   # Première ligne
print(f"{A[0,:]}\n")   # Première ligne
print(f"{A[:,0]}\n")   # Première colonne
print(f"{A.T}\n")   # Transposée de la matrice A

B = A[[0,1]]
print(f"{B}\n")   # Extraire 2 premières lignes

B = A[[1,0,2]]
print(f"{B}\n")   # Echanger les 2 premières lignes

#Fin de l'intro
## Ex 2
import numpy as np
import time

initial_time = time.time()

# Faire ça en plus vite :
list_A = [[0,1,2,3,4],
          [5,6,7,8,9],
          [10,11,12,13,0],
          [15,16,17,18,19],
          [20,21,22,23,24]]
A = np.array(list_A)
print(f"La matrice A vaut\n{A}\n")

print(f"Durée de l'opération manuelle : {abs(initial_time-time.time())}")


initial_time = time.time()
B = np.arange(25)
# print(B)
# print("")
# print(B[14])
B[14] = 0
# print(B[14])
# print(B)
# print(B.shape)
B = np.reshape(B,[5,5])
print(f"La matrice B vaut\n{B}\n")

print(f"Durée de l'opération 'rapide' : {abs(initial_time-time.time())}")

##

A = np.ones([4,4])
A[2,3]=2
A[3,1]=6

print(A)

##

A = np.arange(2,7)
A = np.diag(A)
B = np.zeros(5)
# print(B)

A = np.vstack([B, A])

# print(A)
print(A.astype(int))

##
import numpy as np
import time
import matplotlib.pyplot as plt


NN = [1,2,3,4,5,6,7,8,9,10,20,30,40,50,60,70,80,89,100,200,300,400,500]
TT = []

for N in NN:


    # print(np.diag([5,6,7],k=0))
    # print(np.diag([5,6],k=-1))
    # print(np.diag([5,6],k=+1))

    b = np.ones(N)

    A = np.diag(3*b)+np.diag(np.ones(N-1),k=-1)+np.diag(np.ones(N-1),k=+1)
    # print(A)
    # print(b)

    # Méthode par inversion classique
    initial_time = time.time()
    inv_A = np.linalg.inv(A)
    X = inv_A @ b
    verif = max(abs((A@X)-b))
    # print(f"Erreur : {verif}")
    T0=time.time()-initial_time
    # print(f'Final time = {T0}')

    # Méthode par Numpy
    initial_time = time.time()
    # inv_A = np.linalg.inv(A)
    X = np.linalg.solve(A,b)
    verif = max(abs((A@X)-b))
    # print(f"Erreur : {verif}")
    T1 = time.time()-initial_time
    # print(f'Final time = {T1}')

    # print(f"Ratio T0/T1 : {T0/T1}")
    TT.append(T0/T1)

plt.plot(NN,TT)
plt.show()

## Méthode de l'élimination de Gauss
import numpy as np

list_A = [[1,2,3],
          [4,5,6],
          [6,7,10]]

list_b = [1,2,3]

A = np.array(list_A, dtype=float)
b = np.array(list_b)
print(f"La matrice A vaut\n{A}\n")
print(f"Le vecteur b vaut\n{b}\n")

def Gauss(A,b):
    N = b.size
    assert np.linalg.det(A)!=0, "Matrice singulière !"
    for j in range(N-1): #col
        for i in range(j+1,N): #ligne
            coef = A[i,j]/A[j,j]
            A[i] -= coef*A[j]
            b[i] -= coef*b[j]

    x = np.zeros(N) # Initialisation
    # Remontée!
    for jj in range(N):
        j = N-1-jj
        # print(j)
        second_membre = b[j]
        for k in range(j+1,N):
            second_membre -= A[j,k]*x[k]
        x[j] = second_membre/A[j,j]
    return x

res = Gauss(A,b)

print(A@res)
























##