#########################################
######## TP informatique bio-sép ########
########        2025-2026        ########
#########################################


#########################################
######## Thème 9 : Module numpy #########
#########################################

import numpy as np
import numpy.linalg as la


## Q1 : définir une matrice

ligne1 = [1,2,3,4]
ligne2 = [5,6,7,8]
liste = [ligne1,ligne2]
A = np.array(liste)
print(A)
print(A.shape)

# la commande 'shape' renvoie, sous forme d'un tuple,
# la dimension de la matrice.

## Q2/Q3/Q4 : arange et reshape

X = np.arange(0,10,1)
print(X.shape)
Y = X.reshape(5,2)
print(Y.shape)
B = np.array([1,0,-1,1,-1,1,0,-1,1,1,1,0,0,1,-1,0])
B = B.reshape(4,4)
print(B)

## Q5 : appel d'un élément d'une matrice

print(A[1,3]) # affiche l'élément de ligne 1 et colonne 3
# de la matrice A. Les indices de lignes et de colonnes
# commencent à zéro !

## Q6 : Matrice-lignes ou matrice-colonnes

L = np.array([[1,0,0,0,1]])
C = np.array([[-1,-2,-3]])
C = C.reshape(3,1)

## Q7 : Matrice-ligne avec alternance de 1 et 0

def alterne(n) :
    ligne = [0]*(2*n)
    for k in range(n) :
        ligne[2*k] = 1
    return np.array([ligne])

## Q8 : Matrice de Vandermonde (de suites géométriques)

def c(i,j) :
    return i**j

n,p = 3,3
V = np.array([[c(i,j) for j in range(p)] for i in range(n)])

## Q9 : Matrice de Hilbert

def Hilbert(n) :
    return np.array([[1/(i+j+1) for j in range(n)] for i in range(n)])

## Q10 : extraction de ligne ou de colonne

# à l'aide de la syntaxe A[i,:] pour la ligne d'indice i
# ou A[:,j] pour la colonne d'indice j

## Q11 : altération d'une ligne ou d'une colonne

D = np.array([1 for _ in range(20)])
D = D.reshape(4,5)
D[0,:] = [0 for _ in range(5)]
D[:,3] = [0 for _ in range(4)]

## Q12 : fonctions pour créer des matrices particulières

# matrice nulle de taille (n,p)
Z = np.zeros((2,2))
print(Z)

# matrice remplie de '1'
U = np.ones((4,2))
print(U)

# transposée
print(U.T)

# matrice-identité
I = np.eye(3)
print(I)

# matrice nulle de même taille que A
Z2 = np.zeros_like(A)
U2 = np.ones_like(A)

## Q13 : Produit matriciel

def promat(A,B) :
    n,p = A.shape
    q,r = B.shape
    if p != q :
        return 'tailles incompatibles'
    C = np.zeros((n,r))
    for i in range(n) :
        for j in range(r) :
            s = 0
            for k in range(p) :
                s += A[i,k]*B[k,j]
            C[i,j] = s
    return C

# vérification possible avec la fonction 'np.dot'

## Q14 : puissances de matrices carrées

def puissance(A,n) :
    p,q = A.shape
    if q != p :
        return 'matrice non carrée'
    I = np.eye(p)
    for _ in range(n) :
        I = promat(I,A)
    return I

# vérification possible avec la fonction 'la.matrix_power'

## Q15 : test d'appartenance

def estDansE(M) :
    n,p = M.shape
    if n != 2 or p != 2 :
        return False
    a = M[0,0]
    b = M[1,0]
    return M[0,1] == a+b and M[1,1] == a-2*b

## Q16 : Fonction inversant une matrice

J = np.array([[0,0,1],[1,0,0],[0,1,0]])
K = la.inv(J)
print(K)
print(promat(K,J))

## Q17 : polynôme représenté par une matrice colonne

def estDansH(P) :
    s = 0
    (n,p) = P.shape
    for i in range(n) :
        s += P[i,0]
    return s == 0

## Q18 : formule de changement de base

# une base B de H est :
# B = (X-1, X²-1, X^3-1, X^4-1)
# En effet, ce sont 4 polynômes qui s'annulent en 1
# donc ils appartiennent à H.
# Ils sont de degrés distincts donc ils forment
# une famille libre.
# Cette famille libre est de cardinal 4 donc
# elle engendre un espace de dimension 4
# inclus dans H. Mais la dimension de H ne peut
# pas être 5, qui est celle de R4[X], car
# H est différent de R4[X].

# on complète cette base en une base C de R4[X] :
# C = (1, X-1, X^2-1, X^3-1, X^4-1)
# et on appelle P la matrice de passage
# de la base canonique de R4[X] à C

P = np.array([ [1,0,0,0,0], [-1,1,0,0,0],[-1,0,1,0,0],[-1,0,0,1,0],[-1,0,0,0,1] ])
P = P.T

# pour un polynôme de degré < 5, on donne la matrice
# colonne X de ses coordonnées dans la base canonique

X = np.array([[4,-14,17,-9,2]])
X = X.T

# on obtient la matrice colonne X' de ses coordonnées
# dans la base C en appliquant la formule de
# changement de base : X' = P^{-1}X

Pm1 = la.inv(P)
Xprime = promat(Pm1,X)

# le polynôme appartient à H si et seulement si
# sa première coordonnée dans la base C est nulle.
# Dans ce cas, les autres coordonnées sont celles
# dans la base B.

def coord(Q) :
    Xp = promat(Pm1,Q)
    if Xp[0,0] != 0 :
        return "Q n'est pas dans H"
    return Xp[1:]



































