You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

288 lines
11 KiB

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

import numpy as np
np.set_printoptions(suppress=True) # (pour mieux arrondir à 0 lors des print)
from numpy.polynomial.polynomial import polyval
import math
import matplotlib.pyplot as plt
# On active le "mode interactif" de pyplot. Ainsi, les plt.show() ne sont plus nécessaires.
plt.ion()
# --------------------------------------------------------------------------------------
# Fonction de "fin temporaire" pendant l'avancée du TP. Attend une frappe Entrée, puis quitte le script.
def stop_ici():
plt.pause(0.1) # nécessaire pour que matplotlib ait le temps d'afficher les figures
input()
exit()
# --------------------------------------------------------------------------------------
# Tous les passages indiqués "TODO()" sont à remplacer par vos soins
def TODO():
print("à vous!")
stop_ici()
# --------------------------------------------------------------------------------------
# Cette petite fonction sert à tracer les flèches avec une syntaxe un peu plus intuitive que le code matplotlib de base.
# - xdepart, ydepart : coordonnées du point d'attache de la flèche
# - xfleche, yfleche : longueurs de la flèche en x et en y
def myfleche(xdepart, ydepart, xfleche, yfleche):
plt.annotate("", (xdepart+xfleche,ydepart+yfleche) , (xdepart,ydepart),
arrowprops={'color':'red','shrink': 0,'width':0.02}, annotation_clip=False)
#################################################################################################
###
### Rappels des TP2 et TP3 : courbe paramétrée et approximation affine
###
#################################################################################################
# Une *courbe paramétrée polynômiale* est une courbe paramétrée de la forme
# (P(t), Q(t))
# dont les 2 fonctions de coordonnées sont polynômiales.
#
# - La variable "t" s'appelle le **paramètre** de la courbe.
# - Chaque valeur de t définit UN point, dont l'abscisse vaut P(t) et l'ordonnée vaut Q(t).
# - La courbe est obtenue en représentant les points associés à TOUTES les valeurs de t.
#################################################################################
# Approximation affine pour une courbe polynômiale
#################################################################################
#
# L'approximation affine de la courbe polynômiale au point t s'écrit
#
# P(t+h) ~= P(t) + h.P'(t) [en abscisses]
# Q(t+h) ~= Q(t) + h.Q'(t) [en ordonnées]
#
# On représente cette approximation affine par une FLÈCHE.
#
# - La flèche démarre du point "t" sur le graphe
# Point(t) = ( P(t) , Q(t) )
#
# - La flèche a des longueurs (en abscisses et en ordonnées) de valeurs respectives
# fleche = ( h.P'(t) , h.Q'(t) )
# (Concrètement, la valeur choisie pour h détermine donc la mise à l'échelle de la flèche.)
# Avec ces notations, l'approximation affine ci-dessus peut se réécrire
# Point(t+h) ~= Point(t) + fleche
# c'est-à-dire que "fleche" représente l'évolution (approximative) de la courbe
# lorsque le point d'intérêt passe du paramètre "t" à "t+h".
#################################################################################
#################################################################################################
###
### EXERCICE 5 : Interpolation d'Hermite en 2D
###
#################################################################################################
# Il s'agit exactement du même problème qu'à l'exercice 4, mais pour une courbe polynômiale en 2D.
# On impose un certain nombre de points de passage en 2D, chacun associé à une flèche (= dérivée).
# Le but est de trouver une courbe polynômiale qui passe par ces points, et dont la dérivée à ces points
# soit donnée par les flèches : c'est-à-dire que la courbe doit être tangente à chaque flèche, et évoluer
# d'autant plus "fort" que la flèche est grande.
#
#
# Pour illustrer cela, on a implémenté une petite GUI (fenêtre interactive).
# - les points de passage eux-mêmes sont fixés à une position imposée
# - par contre, les clicks utilisateur permettent de définir la flèche (=dérivée) associée à chaque point
# - au dernier click, la courbe interpolatrice est calculée puis affichée.
print('''Complétez le code de la GUI ci-dessous, afin de calculer et tracer la courbe interpolatrice.''')
plt.close('all') # ferme les fenêtres précédentes, pour y voir plus clair
##############################################################
# Nombre de points à interpoler (laisser à 2 pour l'instant)
N = 2
##############################################################
# Position des N points cible (fixes, le long d'un polygone régulier à N sommets) :
# Elles sont stockées dans le np.array "point", sous le format suivant :
# - point[0,k] renvoie l'abscisse du point cible numéro k
# - point[1,k] renvoie l'ordonnée du point cible numéro k
# - point[0,:] renvoie toutes les abscisses des points cible
# - point[1,:] renvoie toutes les ordonnées des points cible
theta = np.pi - np.arange(0, 2*np.pi, 2*np.pi/N)
point = np.array([np.cos(theta),np.sin(theta)])
# La figure + les points cible :
fig = plt.figure()
limits = (-2,2) # limites de représentation (en x et en y)
ax = fig.add_subplot(111, xlim=limits, ylim=limits)
ax.plot(*point,linestyle='none',marker='o',color='blue')
# Dérivées imposées à chaque point cible (vont être définies par les clicks successifs) :
deriv = np.zeros((2,N))
# Une fois définies, elles seront stockées dans le np.array "deriv", sous le format suivant :
# - deriv[0,k] renvoie l'abscisse de la dérivée cible numéro k
# - deriv[1,k] renvoie l'ordonnée de la dérivée cible numéro k
# - deriv[0,:] renvoie toutes les abscisses des dérivées cible
# - deriv[1,:] renvoie toutes les ordonnées des dérivées cible
# Fonction d'interpolation (recopiée de l'exercice 4)
def interpol_hermite(ys,ds):
TODO()
# La fonction de callback de la GUI (sera appelée à chaque click)
nclick = -1
def onclick(event):
global clickpos, nclick, deriv, ax
clickpos = np.array([event.xdata, event.ydata]) # position du click
nclick = (nclick+1) % (N+2) # incrémente nclick (modulo N+2)*
# Récupère l'objet "axe" associé à la figure. Les commandes graphiques devront être
# appelées directement sur cet objet : "ax.ma_commande(...)" et non pas "plt.ma_commande(...)"
ax = fig.get_axes()[0]
if nclick < N:
### Trace la flèche associée à chaque point
fleche = clickpos - point[:,nclick]
myfleche(*(point[:,nclick]),*fleche)
ax.set(xlim=limits, ylim=limits)
# Écart h de l'approximation affine (fixe la proportionnalité gobale des flèches)
h = 0.2
# stocke la valeur imposée de dérivée correspondante (! en divisant par h !)
deriv[:,nclick] = fleche/h
elif nclick == N:
### au (N+1)ème click, on calcule la courbe interpolatrice et on l'affiche
# --> À VOUS !
# Calcule les poolynômes P(X) et Q(X) par interpolation
p = TODO()
q = TODO()
# Discrétisation de l'intervalle [1,N] en 200 points
t = TODO()
# Trace la courbe
ax.plot(TODO())
else:
### au (N+2)ème click, on nettoie pour pouvoir recommencer le cycle
ax.clear()
ax.plot(*point,linestyle='none',marker='o',color='blue')
ax.set(xlim=limits, ylim=limits)
# La commande ci-dessous "active" la GUI, en connectant la figure "fig" à la fonction de callback "onclick"
cid = fig.canvas.mpl_connect('button_press_event', onclick)
stop_ici() # ---------- Supprimez cette ligne une fois que le code avant fonctionne ------------------
# --------------------------------------------------------------------------------------
print("-"*80)
# Jusqu'à présent, la fonction "interpol_hermite" que vous avez écrite
# n'est capable d'interpoler que 2 points (avec leurs 2 dérivées associées).
#
# Dans ces dernières questions du TP, on vous guide pour réécrire la fonction afin
# qu'elle interpole N points (avec leurs N dérivées associées), pour n'importe
# quelle valeur de N.
print('''(Question papier).
Écrivez la matrice "A" et le second membre "b" du système si vous deviez
interpoler 3 points (et leurs 3 dérivées associées) :
P(1) = y1
P(2) = y2
P(3) = y3
P'(1) = d1
P'(2) = d2
P'(3) = d3
par un polynôme P de degré 5 (c'est-à-dire, avec 6 coefficients inconnus).''')
stop_ici() # ---------- Supprimez cette ligne une fois que le code avant fonctionne ------------------
# --------------------------------------------------------------------------------------
print("-"*80)
print('''(Question papier).
Même question dans le cas général où vous devez interpoler N points (et leurs
N dérivées associées). Trouvez la *formule générale* donnant la valeur de
l'élément de la matrice A en position (i,j).
Indice :
- pour les N premières lignes de A (contrainte de passer par un point yi),
vous avez déjà trouvé cette formule à la séance précédente (partie 1,
interpolation de Lagrange).
- pour les N dernières lignes (contrainte de vérifier une dérivée di), à
vous de jouer!
''')
stop_ici() # ---------- Supprimez cette ligne une fois que le code avant fonctionne ------------------
# --------------------------------------------------------------------------------------
print("-"*80)
print('''Complétez la nouvelle fonction "interpol_hermite(ys,ds)" (ébauche de code ci-dessous),
dans laquelle "ys" et "ds" peuvent maintenant être des tableaux de n'importe quelle taille N.
Enfin testez votre fonction en modifiant le paramètre N de la GUI plus haut (celui qui
valait 2 jusqu'à présent).
''')
### Fonction d'interpolation. Renvoie l'unique polynôme P de degré 2N-1 tel que
# P(1) = y1
# ...
# P(N) = yN
# P'(1) = d1
# ...
# P'(N) = dN
def interpol_hermite(ys,ds):
N = len(ys)
# les N premières lignes de la matrice A
A1 = [ [ TODO() for j in range(2*N) ] for i in range(1,N+1) ]
# les N dernières lignes de la matrice A
A2 = [ [ TODO() for j in range(2*N) ] for i in range(1,N+1) ]
# la matrice A entière
A = TODO()
# le second membre du système linéaire à résoudre
b = TODO()
# la solution du système
p = TODO()
return p
# Remarque : pas besoin d'effacer l'ancienne fonction 'interpol_hermite(ys,ds)'
# que vous avez écrite plus haut. Au moment où vous définissez la nouvelle version
# interpol_hermite(ys,ds), Python la "remplace" purement et simplement (exactement
# comme lorsqu'on redéfinit une variable, du genre v=v+4).
# En Python, les fonctions ne sont rien d'autre que des "variables qu'on peut appeler".
#
# Et puisque vous clickerez dans la GUI *après* avoir défini à Python la nouvelle
# version, la GUI fera naturellement appel à la nouvelle version :)
stop_ici() # ---------- Supprimez cette ligne une fois que le code avant fonctionne ------------------
########################################
# Vous avez tout fini ??? Bravo !!!
# Une fois arrivé ici, si vous vous ennuyez, modifiez le code de la GUI précédente pour
# que les clicks permettent également de définir la *position* des N points de passage.
# Il y aura donc un total de 2*N clicks : une position + une flèche pour chaque point de passage.
# Vous obtiendrez ainsi une GUI permettant de tester la version la plus générale de
# l'interpolation de Hermite (positions+flèches).
input()