J'ai une mauvaise habitude : lorsque je fais quelque chose, je consacre une moitié de mon attention à observer l'action en cours. Cela m'a valu un classement lamentable lors d'un de ces tests qui estiment l'intelligence selon la rapidité des réponses.
Disposant en août d'un peu de temps libre je me suis remis à la programmation et ça m'a permis de faire sur moi-même quelques observations. Il est bon de garder en mémoire ces épisodes où l'on piétine (voir mon apprentissage à LaTeX), cela permet d'éviter de faire plusieurs fois la même erreur.
Les lecteurs expérimentés vont me trouver ridicule car je ne suis ni un programmeur de métier ni même un bon programmeur, mais tant pis. Ceux qui croient que la programmation est une activité ancillaire vont cesser de me lire s'ils m'ont jamais lu, mais tant pis.
* *
Je l'avoue : j'aime programmer. J'y suis un bizut maladroit, pataud, inexpérimenté, mais cette expérience m'apporte les joies de l'exploration. Lorsque ça marche, je suis ravi d'avoir pu plier l'ordinateur à faire ce que je voulais qu'il fît – c'est bien plus satisfaisant que d'utiliser un programme écrit par quelqu'un d'autre.
J'ai donc décidé de programmer les méthodes d'analyse des données que j'ai enseignées à l'ENSAE durant les années 70. A l'époque je ne savais pas programmer : j'utilisais donc les programmes écrits par d'autres et cela me contrariait. Il fallait prendre une revanche sur mon ignorance.
Je savais bien qu'il existe d'excellents logiciels d'analyse des données et que ce que j'allais faire n'apporterait rien de neuf, mais mon but n'était pas de lancer un nouveau produit sur le marché.
Voici, très condensé, le récit de cette aventure.
* *
J'ai besoin d'algorithmes pour inverser une matrice, extraire ses valeurs et vecteurs propres, projeter sur un plan l'image d'un nuage de points plongé dans un espace de quelques dizaines de dimensions, trouver et agréger les points les plus proches selon une « distance » ad hoc etc.
Ce sont là des problèmes de maths. J'ai perdu la virtuosité de mes vingt ans et je serais un piètre candidat aux concours, mais aucun problème ne résiste à une nuit de sommeil : les solutions arrivent le matin, il faut seulement les attendre.
Je commence par l'analyse factorielle. Je programme hâtivement les algorithmes, talonné par la crainte que « ça ne marche pas ». Miracle, ça marche : je sais calculer le premier vecteur propre d'une matrice d'inertie.
Mais je ne dois pas m'en contenter : il me faut le deuxième vecteur propre, le troisième, le quatrième, il me faut même la formule générale qui fournit le n-ème. J'enrichis l'algorithme initial, dont la longueur commence à faire sérieux. Je le complète par le calcul des « facteurs », comme on dit, et par celui des « aides à l'interprétation » (ceux qui connaissent ces choses voient de quoi je parle).
A ce stade le problème mathématique est résolu. Je suis content de moi, car ma connaissance des propriétés de l'analyse factorielle m'a permis d'aller droit au but dans la programmation de formules qui seraient un casse-tête pour un programmeur non mathématicien. Mais je n'en ai pas fini, loin de là.
* *
J'édite donc les facteurs sur les quatre premiers axes. Pour le premier axe, ça va vite. Pour le deuxième, c'est plus lent. Pour le troisième, l'ordinateur piétine. Pour le quatrième, plusieurs très longues secondes se passent entre les affichages de deux nombres successifs – et il y en a, des nombres à afficher !
Ainsi résoudre le problème de maths ne suffit pas car les maths ignorent la durée : une formule exacte étant exacte pour l'éternité, le polytechnicien est fondé à remettre inlassablement la casserole au clou si cela lui permet de « revenir au problème précédent ». Le monde de la pensée s'exprime en termes purement logiques.
Il n'en est pas de même avec le processeur car sa performance est soumise à des contraintes. Il ne suffit donc pas d'être un mathématicien pour écrire un programme efficace : il faut être aussi un physicien, capable de sortir du monde de la pensée pure pour se plier aux contraintes du monde de la nature.
Or mon algorithme, je m'en aperçois, recalcule les mêmes choses : pour extraire le n-ème vecteur propre il recalcule le premier, le deuxième, le troisième etc. jusqu'au (n – 1)-ème.
Pour calculer les n premiers vecteurs propres, il faut donc en fait en calculer n(n + 1)/2... Horreur : mon algorithme est d'ordre n2 ! Après une nuit de sommeil je vois comment passer à l'ordre n : il suffit d'enlever à la matrice d'inertie, lors de chaque étape, le vecteur propre que je viens de calculer.
L'algorithme est devenu beaucoup plus court, j'ai supprimé beaucoup de lignes. Moi qui commençais à me prendre pour un vrai programmeur, capable d'écrire un code impressionnant, me voici ramené à quelque chose de très simple. Je me console en me disant que cette simplicité, c'est de l'élégance. N'aurais-je cependant pas dû l'atteindre du premier coup si seulement j'avais été intelligent ?
* *
Il ne suffit pas d'avoir des résultats à l'écran, dans la moitié basse de l'interface de Racket : il faut aussi éditer le graphique qui rendra visible la projection du nuage de points sur le plan que forme un couple de vecteurs propres (aussi nommés « axes factoriels »).
Alors commence dans la documentation une fouille hagarde. Si l'on peut écrire des algorithmes en utilisant un vocabulaire et une syntaxe logiques et compréhensibles, par contre les interfaces, échanges avec l'écran et le clavier et relations entre divers modules obéissent en effet à un fatras de conventions qu'il faut apprendre par cœur et que la pure logique ne saura jamais deviner.
Pour maîtriser les conventions et astuces d'un langage de programmation jusque dans ses tripes intimes, il faut programmer à temps complet. Celui qui ne programme que quelques semaines par an est irrémédiablement un bizut et la documentation le traite en conséquence : elle est faite pour les professionnels, gens pressés et habitués qui ont appris à la lire selon des notations abrégées elles-mêmes conventionnelles.
Je suis bien incapable de comprendre comment j'ai pu extraire, de tant de pages Web feuilletées, un morceau de code dans lequel il a fallu introduire des paramètres dont j'ai dû chercher le format à tâtons, et que j'ai soumis à un jeu d'essais et d'erreurs jusqu'à l'obtention d'un graphique convenable. Je crois me rappeler que cela m'a pris une demi-journée mais je crains que ma mémoire ne soit infidèle : ce passage dans les soutes obscures a sans doute duré deux ou trois jours...
* *
Arrivé à ce point, j'ai programmé l'analyse factorielle des correspondances. Je vois alors avec horreur que ma hâte m'a encore joué un tour.
Les diverses méthodes d'analyse factorielle – l'analyse des correspondances, l'analyse en composantes principales, l'analyse discriminante (et aussi l'analyse sphérique, ma petite invention) – utilisent toutes les mêmes algorithmes, seule changeant la façon dont est défini le nuage de points. J'aurais dû, si j'avais réfléchi un petit peu, programmer d'abord un module général d'analyse factorielle, puis l'appeler dans de petits programmes particuliers chacun à une de ces méthodes.
Les bras m'en tombent. Il va falloir tout refaire en profondeur, car en écrivant le programme de l'analyse des correspondances j'ai inextricablement entrelacé les notations propres à cette méthode avec celles, plus générales, qui conviendraient à toutes les méthodes...
* *
J'ajoute que Laurent Bloch, qui n'est pas un bizut, a fait sur mon programme une remarque profonde. Pour le lier au fichier qui contient les données à analyser j'ai utilisé la notation qui convient pour agglutiner deux programmes. Ainsi, me dit Laurent, j'ai introduit des données dans le programme et ce n'est pas là du travail propre. Il va falloir que je maîtrise la syntaxe des « imports » et des « exports ».
D'ailleurs je n'ai pour le moment utilisé que l'interpréteur. Il va falloir que je compile mon programme pour fabriquer un exécutable, et avant cela il faudra y introduire le nécessaire pour l'interface avec l'utilisateur : lui demander l'adresse du fichier de données qu'il souhaite « importer » pour l'analyser, la méthode qu'il veut utiliser, le nombre des axes factoriels à extraire, la nature des « aides à l'interprétation » qu'il désire, que sais-je ! Et il faudra en outre éditer les résultats sous une forme lisible : un fichier pdf, par exemple, en passant par une mise en forme sous LaTeX. Puis il faudra rédiger une documentation, même si je suis le seul utilisateur, car j'ai besoin d'un aide-mémoire.
Laurent m'a prévenu : la partie algorithmique du programme, qui est la seule qui procure du plaisir à un mathématicien (et la seule sur laquelle les manuels s'étendent), se compose en quelques jours ; programmer les interfaces graphiques et le dialogue avec l'utilisateur demande par contre des semaines ou des mois d'un labeur ingrat pour obtenir quelque chose qui semblera tout naturel et très simple...
* *
Je comprends pourquoi beaucoup de programmeurs préfèrent compliquer la vie de l'utilisateur ! D'abord ça leur est plus facile, car la mise au point d'une interface commode est un gros travail. Ensuite, n'est-il pas immoral que l'utilisateur n'aie qu'à s'installer dans son fauteuil pour voir tout se passer « naturellement », alors que le programmeur a dû se plier sous le joug de notations et conventions dépourvues de tout naturel ? Ne faut-il pas que l'utilisateur s'en bave, lui aussi ?
Quand on mesure la force de cette tentation on admire les programmeurs qui fournissent des programmes commodes. Ce sont des héros ! Mais seuls ceux qui se sont frottés à la programmation pourront entrevoir cet héroïsme.
C'est pourquoi il importe que l'enseignement ne se focalise pas sur la seule utilisation de l'informatique : pour que de grands artistes puissent éclore, il faut un public de connaisseurs. Nous ne pourrons disposer de bons programmes que si nous en savons assez sur la programmation pour apprécier la belle ouvrage.
Merci pour cet hommage aux programmeurs. J'aime programmer et j'en fais toujours un peu mais c'est comme si c'était une honte si on est manager en France.
RépondreSupprimer