"""
Thème 11 : diagonalisation des matrices carrées

16/12/25

"""

## Importations
import numpy as np
import numpy.linalg as la


## Q1

# on utilise CH10-Prop1.2.

def dimKer(M):
    n,p = M.shape
    r = la.matrix_rank(M)
    return n-r

# Essais :
# une matrice de rang(1)
A = np.ones([3,3])
print(dimKer(A))
# une matrice inversible
I = np.eye(3)
print(dimKer(I))


## Q2

def dimE(A,mu):
    # utilsie le fait que E_mu = Ker(A-muI)
    # et la fonction dimKer de Q1
    n,p = A.shape
    Amu = A-mu*np.eye(n)
    return dimKer(Amu)

## Q3

# on utilise Thm3.3

def est_diago(A,L):
    # on utilise thm3 3. de CH10 :
    # - on calcule la somme  S des dimensions des sev propres (boucle)
    # - on compare la valeur de S à n = nb de lignes de A
    n,p=A.shape
    S = 0        # initialisation de S
    for vp in L:
        S+=dimE(A,vp)
    return S >=n

## Q4

# Plusieurs abus :
# 1. si M n'est pas diagonalisable,
# la juxtaposition de bases des sev propres ne donne pas une matrice P
# inversible, donc on ne peut  qualifier P de matrice de  passage.
#
# 2. Et quand bien même P serait inversible,elle n'est pas unique car
# les bases de vecteurs propres de chaque  sev propres ne sont pas
# uniques.

## Q5

def spectre(M):
    Lvp, P = la.eig(M)
    L =[]
    for elem in Lvp:
        if not elem in L:
            L.append(elem)
    return L

## Q6

# J est triangulaire, donc ses vp sont ses coeffs
# diagonaux. D'où Sp(J)={0}
# Si J était diago, la somme des dimensions des sev
# propres de J vaudrait 3. Or, il y un seul sev
# propre, qui est Ker J. Mais clairement dim Ker J=1
# (puisque rg J = 2 visiblement)
# J IS NOT DIAGONALIZABLE


## Q6a)

# je rentre mes 10 matrices à la main (pas le choix)
L = [0]*10
L[0] = [0,1,0,0,0,1,0,0,0]      # visiblement 0 est l'unique valeur propre
L[1] = [-5,6,4,-4,5,4,2,-2,-3]   # RAS
L[2] = [1,3,2,0,-2,-2,-3,9,8]   # RAS
L[3] = [3,5,-3,0,2,0,6,10,-6]   # 0 et 2 sont visiblement dans le spectre
L[4] = [4,-2,-0,4,-2,0,-2,2,2]  # 0 et 2 aussi sont dans le spectre aussi
L[5] = [0,1,0,-2,3,0,0,0,2]     # 2 est valeur propre
L[6] = [1,4,2,2,3,2,-4,-8,-5]   # RAS
L[7] = [4,1,2,-1,1,-1,-2,-1,0]  # RAS
L[8] = [-1,5,-5,1,-7,6,1,-11,10]# RAS
L[9] = [1,-1,0,1,3,0,-1,-3,1]   # RAS

print("ATTENTION : PROCEDE PAR CALCULS NUMERIQUES, DONC APPROCHES. LES RESULTATS PEUVENT ETRE MATHEMATIQUEMENT FAUX !!!")

for liste in L:
    A = np.array(liste).reshape(3,3) # vous voyez l'intérêt de reshape ?
    print(f"========DIAGONALISATION DE \n {A}\n ===================================")
    sp = spectre(A)
    print (f'Spec(A)={sp}')
##[Q6.b)]
    reponse = est_diago(A,sp)
    print( f"D'après le programmme, A est diagonalisable ? {reponse}")
    toto,P = la.eig(A)
    if reponse:
        print(f"Matrice P qui diagonalise A :P= {P}")
    else:
        print("pas diagonalisable d'après calculs")


# REMARQUES :

# 1. les coordonnées des vecteurs propres calculés sont parfois compliquées :
# python utilise des méthodes euclidiennes qui introduisent des racines carées dans les
# calculs (d'où certaines coordonnées égales à a = 0,707... .Penser à renormaliser
# vos vecteurs propres pour avoir des bases plus simples. Par exemple, si une colonne de
# P vaut :
# U =[[-0.70710678],
#     [-0.70710678],
#     [ 0]]
# on voit que U=[-a,-a,0]T, qu'on peut remplacer évidemment par [1,1,0]T)

# 2. Que la matrice A soit diagonalisable ou pas, Python revoie une matrice P
#    Elle est inversible ssi A est diagonalisable (cf. Q4)
#    Dans ce dernier cas, les colonnes de P fournissent une base de chaque sev propre;
#    Sinon, il y a des redondances, et on aura des famille génératrices des sev propres