Qu’est-ce que la différentiation automatique?

Les modèles mathématiques de la physique regorgent de gradients, divergences, laplaciens, rotationnels, qui sont des dérivées en espace. On y trouve aussi des dérivées en temps pour les problèmes instationnaires. La dérivation est omniprésente.

Ce dont nous allons parler maintenant est plus général et vise jusqu’à la dérivation des modèles eux-mêmes par rapport à des variables diverses.

Motivation

Une telle dérivation intervient dès l’établissement des équations lorsque celles-ci dérivent d’un potentiel, ou bien pour leur résolution basée sur les informations données par le jacobien: le modèle est linéarisé.

Au-delà de la résolution de l’équation, l’optimisation des paramètres en vue d’un critère sur la solution tirera profit de la connaissance des dérivées de celle-ci par rapport à ceux-là, appelés encore sensibilité de la solution par rapport aux paramètres.

Entrons dans les détails bien connus des spécialistes de l’optimisation. Pour l’optimisation d’ordre 1, on observe que seule la variation efficace vis-à-vis du critère compte, c’est-à-dire la contraction de la variation de la solution avec le gradient du critère par rapport à la solution. Mais la variation de la solution résulte de la résolution d’un système linéaire que l’on peut choisir de transposer mathématiquement sur le gradient du critère. C’est la méthode adjointe, plus performante dès que le nombre de critères à optimiser est inférieur au nombre de paramètres de dérivation… En optimisation d’ordre supérieur, la combinaison de l’adjoint et des sensibilités sera utile.

Des méthodes très différentes

Qu’il s’agisse de variables inconnues ou de données, une fois discrétisées, le problème consiste à dériver une formule mathématique par rapport à une liste de ses entrées de calcul. Partant d’un code de calcul produisant le résultat de la formule mathématique à dériver, il existe trois méthodes pour développer le code fournissant la dérivée.

1. Différence finie

C’est la plus simple des méthodes: elle consiste à réutiliser le code original en faisant varier légèrement le paramètre d’entrée considéré et à faire la différence entre les deux résultats, que l’on divise par l’écart de paramètre. Avec un deuxième calcul pris en une valeur avec l’écart opposé, on obtient un résultat précis non plus au premier ordre mais au second.

Hélas les mathématiques sont contredites par l’informatique dont les arrondis vont empêcher les différences de se montrer précises, car les décimales en jeu seront de plus en plus faibles mais toujours relatives à la même valeur centrale. La précision est donc nécessairement limitée. De plus, le choix de l’écart -le fameux epsilon- pourra parfois n’être jamais satisfaisant si la quantité à dériver est une somme dont les termes sont d’ordre de grandeur différent sans relation avec l’ordre de grandeur de leurs variations… Le petit terme de variation importante sera écrasé et la dérivée perdra sa pertinence.

La méthode semi-analytique

Lorsque la solution provient de la résolution d’un système linéaire, plutôt que de faire la différence entre les solutions, il est préférable de la faire sur les seconds membres et de résoudre le système correspondant. C’est plus rapide et plus performant. Il faut bien sûr soit avoir une matrice constante soit être arrivé à convergence dans le cas linéarisé d’un algorithme de Newton. Voilà ce que dit l’analyse: on peut reporter la dérivation sur le second membre. Si celui-ci est calculé par différence finie, la méthode est dite semi-analytique.

Encore faut-il que le code ne soit plus tout-à-fait une boîte noire: il faut accéder au résultat du second membre avant la résolution du système linéaire. On reste donc à haut-niveau. La méthode de calcul de sensibilité dite semi-analytique est proposée par certains éditeurs.

2. La différentiation de code ligne-à-ligne (algorithmique)

C’est théoriquement le plus simple d’usage, grâce aux outils de différentiation automatique de code: on fournit le code, dont on précise la sortie et l’entrée, et l’outil génère le code calculant la dérivée. Pour cela, ligne après ligne, l’outil écrit le code contenant valeur et dérivée – car on sait dériver les fonctions de base – par chaînage direct ou inverse (adjoint). D’abord réservé au Fortran pour son absence de pointeurs, la méthode a été étendue au C++ par d’autres outils qui ont tiré profit de la surcharge d’opérateurs du langage pour minimiser la ré-ingénierie du code original. Les opérations sont en effet surchargées pour produire valeur et chaînage de dérivée au lieu de la valeur seule.

La méthode de différentiation algorithmique a tout son intérêt dans le cas de codes hérités complexes car, s’appuyant sur les mêmes calculs, elle évite le débogage d’un code nouveau. Elle demande néanmoins une assistance humaine experte pour être opérationnelle et efficace.

Le principe de la méthode semi-analytique doit absolument être employé pour éviter la dérivation ligne-à-ligne de la résolution du système linéaire. Voilà un exemple d’assistance humaine indispensable.

3. Dérivation symbolique de la formule

Dans l’idéal, on voudrait dériver symboliquement la formule et coder son calcul. Le faire à la main conduit à s’octroyer un temps de développement qu’on s’interdisait, tandis que passer par un logiciel de calcul formel a quelques inconvénients majeurs.

  • D’une part, il est difficile d’exprimer la formule quand elle s’appuie sur des concepts complexes comme les éléments finis et les maillages.
  • D’autre part, le résultat développé perd la structure tensorielle dont elle est issue et ne donne pas les performances optimales. L’optimisation de la génération de code par ces outils repose en effet sur l’identification de la répétition d’opérations de base comme les multiplications et les sommes de variables scalaires, alors qu’il y a mieux à faire, algébriquement parlant.

Selon les auteurs de la journée Algorithmic Differentiation Workshop organisée par le Groupe de Recherche GDR-Calcul du CNRS le 24 janvier 2019 à Paris, cela peut conduire à une explosion de la complexité des expressions.

C’est donc un défi…

Moderniser son code de simulation?

L’occasion d’entreprendre la modernisation des processus de développements peut être provoquée par

  • la volonté d’accroître sa productivité, de réduire sa maintenance, de se focaliser sur ses atouts, de capitaliser sur ses développements
  • les besoins en nouvelles formulations et les calculs de sensibilité
  • les besoins en assemblages très rapides.
Aux commandes de Navpactos

C’est sans doute l’occasion de considérer l’utilisation de NAVPACTOS, langage spécialement développé par Naupacte pour ces besoins-là. En effet, en débarrassant les codes de leur partie calculatoire, NAVPACTOS allège considérablement leur mise au point. Qu’en est-il alors de la performance? Loin d’être dégradées, les vitesses d’assemblage notamment sont inédites.

La programmation d’un assemblage par Éléments Finis est un exemple où NAVPACTOS brille par sa simplicité et sa performance.

La simplification de la réécriture n’est pas le seul atout de ce langage, car il ouvre la voie d’une programmation générique avec une grande réutilisabilité. En effet, sa puissance vient de ce qu’il est capable de transférer des formulations – ou définitions de calcul – grâce à son moteur de calcul formel tensoriel. Que l’on songe aux fonctionnalités qui manient de telles formulations: l’assemblage, les champs, les maillages, les algorithmes comme celui de Newton… et par conséquent les modules de plus haut niveau qui les utilisent. Tout le code basé sur NAVPACTOS bénéficie du concept de calcul formel.

Une ouverture possible permise par la réécriture avec NAVPACTOS est le développement d’un algorithme d’optimisation paramétrique utilisant les gradients de manière automatique.

NAVPACTOS est donc un outil à considérer lorsque la question se pose de moderniser son code de calcul.

Avec NAVPACTOS, vous pourrez, à partir des spécifications d’un modèle numérique, lues par exemple sur un fichier de commandes, composer facilement sa formulation et la construction du système correspondant, pour ensuite les calculer puis les mettre à jour au moyen d’un simple appel. Un langage dédié écrit en C++ simple et expressif vous permet de suivre le modèle mathématique.

Il suffit de compiler comme d’habitude votre programme de construction et de résolution faisant appel à NAVPACTOS. La performance exceptionnelle des calculs demandés est délivrée automatiquement grâce à une méthode inédite basée sur quelques milliers de règles internes.