Lancement de la version 14.2 de Wolfram Language & Mathematica : Big Data rencontre le calcul et l’IA
Le rythme soutenu des nouvelles versions continue…
Il y a un peu moins de six mois (176 jours, pour être précis), nous avons lancé la version 14.1. Aujourd’hui, j’ai le plaisir d’annoncer la sortie de la version 14.2, qui apporte les dernières avancées de notre département R&D.
C’est une période passionnante pour notre technologie, tant en ce qui concerne ce que nous sommes désormais capables de mettre en œuvre que la manière dont notre technologie est utilisée dans le monde. Une tendance marquante de cette époque est l’utilisation croissante du Wolfram Language, non seulement par des humains, mais aussi par des intelligences artificielles. Il est très gratifiant de constater que tous les efforts que nous avons consacrés au fil des années à la conception cohérente du langage, à son implémentation et à sa documentation portent aujourd’hui leurs fruits : le Wolfram Language devient un outil particulièrement précieux pour les IA, en complément de leurs capacités intrinsèques.
Mais il y a aussi une autre facette de l’IA. Avec notre Wolfram Notebook Assistant, lancé le mois dernier, nous utilisons la technologie d’IA (ainsi que bien d’autres éléments) pour offrir une interface conversationnelle avec le Wolfram Language. Comme je l’ai décrit lors de la sortie du Wolfram Notebook Assistant, c’est un outil extrêmement utile, aussi bien pour les experts que pour les débutants. Mais je pense qu’à terme, sa conséquence la plus importante sera d’accélérer la transition de n’importe quel domaine X vers un “X computationnel” — en tirant parti de l’ensemble de la technologie que nous avons construite autour du Wolfram Language.
Alors, quoi de neuf dans la version 14.2 ? En coulisses, plusieurs modifications ont été apportées pour rendre le Wolfram Notebook Assistant plus efficace et plus fluide. Mais il y a également de nombreuses améliorations visibles et extensions apportées aux éléments du Wolfram Language accessibles à l’utilisateur. Au total, 80 nouvelles fonctions ont été ajoutées — et 177 fonctions existantes ont été largement mises à jour.
Certaines nouveautés prolongent des projets de R&D de longue date, comme l’ajout de fonctionnalités pour la vidéo ou l’extension des capacités liées aux tableaux symboliques. D’autres introduisent des domaines entièrement nouveaux au sein du langage, comme la théorie des jeux. Mais le plus grand développement de la version 14.2 concerne la gestion des données tabulaires, et en particulier des données tabulaires volumineuses. Il s’agit d’un tout nouveau sous-système du Wolfram Language, qui a des répercussions puissantes dans l’ensemble de l’environnement. Cela fait plusieurs années que nous y travaillons, et nous sommes ravis de pouvoir enfin le proposer avec la version 14.2.
En parlant de nouvelles fonctionnalités : il y a plus de sept ans, nous avons été pionniers dans le concept de conception logicielle ouverte, en diffusant en direct nos réunions de conception. Depuis la sortie de la version 14.1, nous avons ainsi réalisé 43 sessions de conception logicielle en livestream, pour un total de 46 heures (j’ai également animé 73 heures d’autres diffusions en direct pendant cette période). Certaines des fonctionnalités incluses dans la version 14.2 ont été amorcées il y a plusieurs années. Mais comme nous diffusons depuis longtemps, presque tout ce qui se trouve dans cette nouvelle version a été conçu en direct, publiquement, à un moment donné. Concevoir un logiciel est un travail exigeant (comme vous pouvez le constater en regardant les livestreams). Mais c’est toujours passionnant de voir les résultats concrets de ces efforts se matérialiser dans le système que nous construisons, étape par étape, depuis tant d’années. C’est donc avec beaucoup de plaisir que nous lançons aujourd’hui la version 14.2, et que nous permettons à tous d’explorer ce que nous avons mis tant d’énergie à développer.
Assistant de discussion intégré dans n’importe quel carnet (Notebook)
Le mois dernier, nous avons lancé le Wolfram Notebook Assistant pour « transformer les mots en calculs » — et aider aussi bien les experts que les débutants à exploiter plus largement et plus en profondeur la technologie du Wolfram Language. Dans la version 14.1, le principal moyen d’utiliser le Notebook Assistant était via une fenêtre séparée de type « chat latéral ».
Mais dans la version 14.2, les cellules de discussion (chat cells) deviennent une fonctionnalité standard de tout carnet, accessible à toute personne disposant d’un abonnement au Notebook Assistant.
Il suffit de taper un ‘ comme premier caractère d’une cellule pour la transformer en cellule de discussion :
Vous pouvez désormais commencer à discuter avec le Notebook Assistant :

Avec le chat latéral, vous avez un « canal séparé » pour communiquer avec le Notebook Assistant, qui ne sera pas, par exemple, sauvegardé avec votre carnet. Avec les cellules de discussion, votre conversation devient une partie intégrante du carnet.
Nous avons en fait introduit les Chat Notebooks au milieu de l’année 2023 — quelques mois seulement après l’arrivée de ChatGPT. Les Chat Notebooks définissaient l’interface, mais à l’époque, le contenu des cellules de chat provenait uniquement de modèles de langage externes (LLM).
Aujourd’hui, dans la version 14.2, les cellules de discussion ne sont plus limitées aux Chat Notebooks séparés, mais sont disponibles dans n’importe quel carnet. Par défaut, elles utilisent l’ensemble de la technologie du Notebook Assistant, qui va bien au-delà d’un simple modèle de langage. De plus, une fois que vous avez un abonnement Notebook Assistant + LLM Kit, vous pouvez utiliser les cellules de discussion de manière fluide, sans avoir besoin d’un compte auprès de fournisseurs externes de LLM.
La fonctionnalité des cellules de discussion dans la version 14.2 hérite de toutes les caractéristiques des Chat Notebooks. Par exemple, taper un ~ dans une nouvelle cellule crée une pause de discussion, vous permettant de démarrer une « nouvelle conversation ». Et lorsque vous utilisez une cellule de discussion, celle-ci peut accéder à tout le contenu de votre carnet jusqu’à la dernière pause de discussion. (À noter que lorsque vous utilisez le Notebook Assistant via le chat latéral, il peut également voir la sélection que vous avez faite dans le carnet sur lequel vous êtes concentré.)
Par défaut, les cellules de discussion « dialoguent » avec le Notebook Assistant. Mais si vous le souhaitez, vous pouvez aussi les utiliser pour communiquer avec des LLM externes, comme dans notre premier Chat Notebook — un menu pratique permet de configurer cela facilement. Bien entendu, en utilisant un LLM externe, vous ne bénéficiez pas de toutes les technologies désormais intégrées au Notebook Assistant, et sauf si vous effectuez des recherches spécifiques sur les LLM, vous trouverez généralement bien plus utile et pertinent d’utiliser les cellules de discussion dans leur configuration par défaut — c’est-à-dire en dialoguant avec le Notebook Assistant.
Apportez-nous vos gigaoctets ! Présentation de Tabular
Listes, associations, datasets : ce sont des moyens très flexibles de représenter des collections de données structurées dans le Wolfram Language. Mais avec la version 14.2, un nouveau type fait son apparition : Tabular.
Tabular offre une manière fluide et ultra-efficace de gérer des tableaux de données organisés en lignes et colonnes. Et quand on dit « efficace », on veut dire qu’il peut manipuler des gigaoctets de données ou plus, que ce soit en mémoire ou hors mémoire (out of core), de manière totalement fluide.
Voyons un exemple. Commençons par importer des données tabulaires :

Il s’agit de données sur les arbres de la ville de New York — 683 788 arbres, chacun possédant 45 propriétés (parfois manquantes).
La structure Tabular introduit plusieurs concepts nouveaux. L’un d’eux est la possibilité de traiter les colonnes tabulaires comme des variables à part entière.
Voici un exemple : nous utilisons cette fonctionnalité pour générer un histogramme des valeurs de la colonne « tree_dbh » (diamètre des arbres) dans cette instance de Tabular :

On peut considérer un objet Tabular comme une forme optimisée d’une liste d’associations, où chaque ligne est une association dont les clés sont les noms des colonnes.
Des fonctions comme Select fonctionnent alors directement sur un Tabular, de façon naturelle et fluide :

La fonction Length renvoie le nombre de lignes du Tabular :

La fonction CountsBy traite un Tabular comme une liste d’associations, en extrayant la valeur associée à la clé « spc_latin » (nom latin de l’espèce) dans chaque association, et en comptant combien de fois chaque valeur apparaît.
(Ici, « spc_latin » est une abréviation de l’expression fonctionnelle # »spc_latin »&.)

Pour obtenir les noms des colonnes, on peut utiliser la nouvelle fonction ColumnKeys:

En considérant un Tabular comme une liste d’associations, on peut en extraire des parties — en donnant d’abord une spécification des lignes, puis une spécification des colonnes :

Grâce à l’introduction de Tabular, de nombreuses nouvelles opérations ont pu être ajoutées.
Un exemple est la fonction AggregrateRows, qui permet de créer un nouveau Tabular à partir d’un Tabular existant, en regroupant des lignes selon une certaine valeur — ici, celles ayant la même valeur dans « spc_latin » — puis en appliquant une fonction à chaque groupe — ici, la moyenne des valeurs de « tree_dbh » :

Une opération comme ReverseSortBy fonctionne alors directement sur ce tableau, ici pour trier en ordre inverse selon la valeur de « meandbh » :

Ici, nous créons une matrice ordinaire à partir d’une petite portion de données extraites de notre Tabular :

Et maintenant, nous pouvons tracer le résultat, en représentant les positions des pins de Virginie à New York :

Quand devriez-vous utiliser un Tabular, plutôt qu’un Dataset par exemple ?
Tabular est spécifiquement conçu pour les données organisées en lignes et colonnes, et il prend en charge de nombreuses opérations puissantes adaptées à ce type de structure « rectangulaire ».
Dataset est plus général ; il peut avoir une hiérarchie arbitraire de dimensions de données, et ne peut donc pas, en règle générale, prendre en charge toutes les opérations « rectangulaires » disponibles avec Tabular.
De plus, étant spécialisé dans les données « rectangulaires », Tabular peut également être bien plus efficace, et nous utilisons les dernières méthodes spécifiques aux types pour gérer des données à grande échelle.
Si vous utilisez TabularStructure, vous pouvez voir certains des éléments qui rendent Tabular si efficace.
Chaque colonne est traitée comme des données d’un type spécifique (et oui, les types sont cohérents avec ceux utilisés dans le compilateur du Wolfram Language).
Il y a aussi un traitement optimisé des données manquantes, avec plusieurs nouvelles fonctions ajoutées spécifiquement pour gérer ce cas.

Ce que nous avons vu jusqu’à présent concerne Tabular fonctionnant avec des données en mémoire (« in-core »). Mais vous pouvez également utiliser Tabular de manière transparente avec des données hors mémoire (« out-of-core »), par exemple des données stockées dans une base de données relationnelle.
Voici un exemple de ce à quoi cela ressemble :

Il s’agit d’un Tabular qui pointe vers une table dans une base de données relationnelle. Par défaut, il ne affiche pas explicitement les données dans le Tabular (et en fait, il ne les charge même pas en mémoire, car elles peuvent être volumineuses et changer rapidement). Cependant, vous pouvez toujours spécifier des opérations exactement comme sur n’importe quel autre Tabular. Par exemple, cela permet de découvrir les colonnes présentes dans la table :

Et cela spécifie une opération, en donnant le résultat sous forme d’un objet Tabular symbolique hors mémoire (« out-of-core ») :

Vous pouvez résoudre cela et obtenir un Tabular explicite en mémoire en utilisant la fonction ToMemory:

Manipuler les données dans Tabula
Supposons que vous ayez un Tabular — comme celui-ci basé sur des pingouins :

Il existe de nombreuses opérations que vous pouvez effectuer pour manipuler les données dans ce Tabular de manière structurée, ce qui vous renverra un autre Tabular. Par exemple, vous pourriez simplement prendre les 2 dernières lignes du Tabular :

Ou bien, vous pourriez échantillonner 3 lignes aléatoires :

D’autres opérations dépendent du contenu réel du Tabular. Et puisque vous pouvez traiter chaque ligne comme une association, vous pouvez créer des fonctions qui font référence aux éléments en utilisant leurs noms de colonnes :

Notez que nous pouvons toujours utiliser # [name] pour faire référence aux éléments d’une colonne. Si name est une chaîne alphanumérique, nous pouvons également utiliser l’abréviation #name. Et pour d’autres chaînes, nous pouvons utiliser # »name ». Certaines fonctions vous permettent même d’utiliser simplement « name » pour indiquer la fonction #[« name »]:

Jusqu’à présent, nous avons parlé uniquement de l’agencement ou de la sélection des lignes dans un Tabular. Mais qu’en est-il des colonnes ? Voici comment nous pouvons construire un Tabular qui contient seulement deux des colonnes de notre Tabular original :

Que faire si nous ne voulons pas simplement des colonnes existantes, mais plutôt de nouvelles colonnes qui sont des fonctions de celles-ci ? ConstructColumns nous permet de définir de nouvelles colonnes, en précisant leurs noms et les fonctions à utiliser pour calculer les valeurs qu’elles contiendront :

(Notez l’astuce consistant à écrire Function pour éviter d’avoir à mettre des parenthèses, comme dans « species »(StringTake[#species, 1]&).)
ConstructColumns vous permet de prendre un Tabular existant et de créer un nouveau Tabular.
TransformColumns vous permet de transformer les colonnes d’un Tabular existant, ici en remplaçant les noms des espèces par leurs premières lettres :

TransformColumns vous permet également d’ajouter de nouvelles colonnes, en spécifiant leur contenu de la même manière que dans ConstructColumns.
Mais où TransformColumns place-t-il vos nouvelles colonnes ? Par défaut, elles sont ajoutées à la fin, après toutes les colonnes existantes. Cependant, si vous listez spécifiquement une colonne existante, cela sera utilisé comme marqueur pour déterminer où placer la nouvelle colonne.
Par exemple, « name » Nothing permet de supprimer une colonne:

Tout ce que nous avons vu jusqu’à présent fonctionne séparément sur chaque ligne d’un Tabular.
Mais que faire si nous voulons “absorber” toute une colonne pour l’utiliser dans notre calcul—par exemple, calculer la moyenne d’une colonne entière, puis la soustraire à chaque valeur ?
ColumnwiseValue permet cela, en fournissant à la fonction (ici Mean) une liste de toutes les valeurs de la ou des colonnes spécifiées :

ColumnwiseValue vous permet de calculer une valeur scalaire en appliquant une fonction à une colonne entière.
Il existe aussi ColumnwiseThread, qui vous permet de calculer une liste de valeurs — laquelle sera ensuite intégrée (ou “enchaînée”) dans une colonne.
Ici, par exemple, nous créons une nouvelle colonne à partir d’une liste de valeurs cumulées :

Au passage, comme nous le verrons plus loin, si vous avez généré en externe une liste de valeurs (de la bonne longueur) que vous souhaitez utiliser comme colonne, vous pouvez le faire directement en utilisant la fonction InsertColumns.
Il y a un autre concept très utile en pratique lorsqu’on travaille avec des données tabulaires : le regroupement.
Dans notre exemple de données sur les manchots, nous avons une ligne individuelle pour chaque manchot de chaque espèce. Mais que faire si l’on souhaite plutôt regrouper tous les manchots d’une espèce donnée, par exemple pour calculer leur masse corporelle moyenne ? Eh bien, nous pouvons le faire avec AggregateRows.
AggregateRows fonctionne un peu comme ConstructColumns, dans le sens où vous spécifiez les colonnes et leur contenu.
Mais contrairement à ConstructColumns, il crée de nouvelles lignes « agrégées » :

Quelle est cette première colonne ici ? Le fond gris de ses entrées indique qu’il s’agit de ce que nous appelons une « colonne clé » : une colonne dont les entrées (éventuellement combinées avec d’autres colonnes clés) peuvent être utilisées pour référencer des lignes.
Et plus tard, nous verrons comment vous pouvez utiliser RowKey pour désigner une ligne en donnant une valeur issue d’une colonne clé :

Mais poursuivons nos efforts d’agrégation. Disons que nous voulons regrouper non seulement par espèce, mais aussi par île. Voici comment nous pouvons le faire avec AggregateRows:

D’une certaine manière, ce que nous avons ici est un tableau dont les lignes sont spécifiées par des paires de valeurs (ici « species » et « island »). Mais il est souvent pratique de « pivoter » les choses afin que ces valeurs soient utilisées respectivement pour les lignes et les colonnes. Et vous pouvez faire cela avec PivotTable:

Remarquez les « — », qui indiquent des valeurs manquantes ; apparemment, il n’y a pas de manchots Gentoo sur l’île Dream, etc.
PivotTable donne normalement exactement les mêmes données que AggregateRows, mais sous une forme réorganisée. Une fonctionnalité supplémentaire de PivotTable est l’option IncludeGroupAggregates, qui inclut des entrées All (Toutes) permettant d’agréger les données à travers chaque type de groupe.

Si vous avez plusieurs fonctions que vous calculez, AggregateRows les affichera simplement sous forme de colonnes séparées :

PivotTable peut également gérer plusieurs fonctions en créant des colonnes avec des « clés étendues » :

Et maintenant, vous pouvez utiliser RowKey et ExtendedKey pour référencer les éléments du Tabular résultant :

Importer des données dans Tabular
Nous avons vu certaines des opérations possibles une fois que vous avez des données sous forme de Tabular. Mais comment importer des données dans un Tabular ? Il existe plusieurs méthodes.
La première consiste simplement à convertir des structures comme des listes ou des associations.
La deuxième méthode est d’importer à partir d’un fichier, comme un CSV ou un XLSX (ou, pour de plus grandes quantités de données, un fichier Parquet) — ou encore depuis un stockage de données externe (comme S3, Dropbox, etc.).
La troisième méthode consiste à se connecter à une base de données.
Vous pouvez également obtenir des données pour un Tabular directement depuis la Wolfram Knowledgebase ou le Wolfram Data Repository.
Souhaitez-vous aussi la traduction des exemples de code associés ?
Voici comment vous pouvez convertir une liste de listes en un Tabular:

Et voici comment vous pouvez convertir en sens inverse (revenir depuis un Tabular vers une autre structure):

Cela fonctionne également avec des tableaux creux (sparse arrays), ici en créant instantanément un Tabular d’un million de lignes

ce qui prend 80 Mo à stocker :

Voici ce qui se passe avec une liste d’associations:

Vous pouvez obtenir le même Tabular en saisissant ses données et ses noms de colonnes séparément :

Au fait, vous pouvez convertir un Tabular en Dataset

Et dans ce cas simple, vous pouvez aussi le reconvertir en Tabular

En général, cependant, il existe toutes sortes d’options pour convertir des listes, des datasets, etc. en objets Tabular — et ToTabular est conçu pour vous permettre de contrôler cela. Par exemple, vous pouvez utiliser ToTabular pour créer un Tabular à partir de colonnes plutôt que de lignes:

Et pour les données externes ? Dans la version 14.2, Import prend désormais en charge un élément « Tabular » pour les formats de données tabulaires. Par exemple, avec un fichier CSV:
Import peut immédiatement l’importer sous forme de Tabular :

Cela fonctionne de manière très efficace, même pour des fichiers CSV énormes contenant des millions d’entrées. Il identifie également très bien les noms de colonnes et les en-têtes automatiquement. Le même principe s’applique aux fichiers plus structurés, comme ceux provenant de feuilles de calcul ou de formats de données statistiques. Et cela fonctionne aussi avec les formats modernes de stockage en colonnes comme Parquet, ORC et Arrow.
Import gère de manière transparente aussi bien les fichiers ordinaires que les URL (et URI), en demandant une authentification si nécessaire. Dans la version 14.2, nous introduisons le nouveau concept de DataConnectionObject, qui fournit une représentation symbolique des données distantes, encapsulant essentiellement tous les détails nécessaires pour accéder aux données. Par exemple, voici un DataConnectionObject pour un bucket S3, dont nous pouvons immédiatement importer le contenu :

(Dans la version 14.2, nous prenons en charge Amazon S3, Azure Blob Storage, Dropbox, IPFS — avec de nombreuses autres intégrations à venir. Nous prévoyons également la prise en charge des connexions aux entrepôts de données, aux API, etc.)
Mais qu’en est-il des données trop volumineuses — ou évoluant trop rapidement — pour qu’il soit pertinent de les importer explicitement ? Une fonctionnalité importante de Tabular (mentionnée précédemment) est sa capacité à gérer de manière transparente des données externes, par exemple dans des bases de données relationnelles.
Voici une référence à une grande base de données externe:
Ceci définit un Tabular qui pointe vers une table dans la base de données externe :
Nous pouvons demander les dimensions du Tabular — et nous voyons qu’il contient 158 millions de lignes :
Le tableau que nous examinons contient, en l’occurrence, toutes les données linéaires d’OpenStreetMap. Voici les 3 premières lignes et les 10 premières colonnes :
La plupart des opérations sur le Tabular seront maintenant effectuées directement dans la base de données externe. Ici, nous demandons de sélectionner les lignes dont le champ « name » contient « Wolfram »:
Le calcul réel n’est effectué que lorsque nous utilisons ToMemory, et dans ce cas (en raison de la grande quantité de données dans la base de données), cela prend un peu de temps. Mais bientôt, nous obtenons le résultat sous forme de Tabular :
Et nous apprenons qu’il y a 58 éléments portant le nom « Wolfram » dans la base de données.
Une autre source de données pour Tabular est la base de connaissances intégrée de Wolfram. Dans la version 14.2, EntityValue prend en charge la sortie directe sous forme de Tabular :

La base de connaissances de Wolfram offre de nombreux exemples intéressants de données pour Tabular. Il en va de même pour le Wolfram Data Repository, où vous pouvez généralement appliquer Tabular pour obtenir des données sous forme de Tabular

Nettoyage des données pour les tableau
C’est, à bien des égards, le fléau de la science des données. Certes, les données sont sous forme numérique. Mais elles ne sont pas propres ; elles ne sont pas exploitables par l’ordinateur. Le Wolfram Language est depuis longtemps un outil exceptionnellement puissant pour nettoyer les données de manière flexible (et, par exemple, pour progresser à travers les dix niveaux de transformation des données en données exploitables par l’ordinateur que j’ai définis il y a quelques années).
Mais maintenant, avec la Version 14.2 et Tabular, nous disposons d’un tout nouveau lot de fonctionnalités simplifiées pour le nettoyage des données. Commençons par importer des données « brutes » (et, en réalité, cet exemple est plus propre que beaucoup d’autres) :

(Au fait, s’il y avait vraiment des éléments aberrants dans le fichier, nous aurions peut-être voulu utiliser l’option MissingValuePattern pour spécifier un motif qui remplacerait immédiatement ces éléments aberrants par Missing[…].)
D’accord, mais commençons par examiner ce qui a été importé depuis notre fichier, en utilisant TabularStructure:

On voit que Import a réussi à identifier correctement le type de données de base dans la plupart des colonnes — même s’il ne peut pas, par exemple, déterminer si des nombres sont de simples valeurs numériques ou s’ils représentent des quantités avec unités, etc. Il identifie également que certaines entrées dans certaines colonnes sont « manquantes ».
Comme première étape du nettoyage des données, supprimons ce qui semble être une colonne « id » sans pertinence :

Ensuite, on remarque que les éléments de la première colonne sont identifiés comme des chaînes de caractères — alors qu’il s’agit en réalité de dates, qui devraient être combinées avec les heures de la deuxième colonne. On peut faire cela avec TransformColumns, en supprimant la colonne devenue « superflue » en la remplaçant par Nothing:

En regardant les différentes colonnes numériques, on constate qu’il s’agit en réalité de quantités qui devraient avoir des unités. Mais d’abord, pour plus de clarté, renommons les deux dernières colonnes :

Transformons maintenant les colonnes numériques en colonnes de quantités avec unités et, tant qu’à faire, convertissons aussi les valeurs de °C en °F :

Voici comment nous pouvons maintenant tracer la température en fonction du temps :

Il y a beaucoup de fluctuations dans ce graphique. Et en regardant les données, on voit que les valeurs de température proviennent de plusieurs stations météorologiques différentes. Voici comment sélectionner les données d’une seule station:

À quoi correspond l’interruption dans la courbe ? Si l’on fait simplement défiler jusqu’à cette partie du tableau, on verra que cela est dû à des données manquantes :

Alors, que peut-on faire à ce sujet ? Eh bien, il existe une fonction puissante, TransformMissing, qui offre de nombreuses options. Ici, nous lui demandons d’interpoler afin de remplir les valeurs de température manquantes :

Et maintenant, il n’y a plus de lacunes, mais, de manière un peu mystérieuse, le graphique s’étend un peu plus loin :

La raison, c’est que l’interpolation est effectuée même dans les cas où pratiquement aucune mesure n’a été faite. Nous pouvons supprimer ces lignes à l’aide de Discard :

Et maintenant, nous n’aurons plus ce « débordement » à la fin :

Parfois, certaines données seront explicitement manquantes ; d’autres fois (de manière plus insidieuse), les données seront simplement incorrectes. Regardons l’histogramme des valeurs de pression dans nos données :

Oups. Que sont ces petites valeurs ? Elles sont probablement incorrectes (peut-être des erreurs de saisie ?). Nous pouvons supprimer ces valeurs « anormales » en utilisant TransformAnomalies. Ici, nous lui demandons d’éliminer complètement toute ligne où la pression était jugée « anormale » :

On peut aussi demander à TransformAnomalies d’essayer de « corriger » les données. Ici, nous remplaçons simplement toute valeur de pression anormale par la dernière valeur de pression précédente dans le tableau :

On peut aussi demander à TransformAnomalies de « marquer » toute valeur anormale et de la rendre « manquante ». Mais que se passe-t-il si l’on essaie ensuite de faire des calculs avec ces valeurs manquantes ? C’est là que MissingFallback entre en jeu. C’est une fonction fondamentalement très simple — elle renvoie simplement son premier argument qui n’est pas manquant :

Mais même si elle est simple, MissingFallback est essentielle pour faciliter la gestion des valeurs manquantes. Par exemple, voici comment calculer une vitesse vers le nord (northspeed), en revenant à 0 si les données nécessaires au calcul sont manquantes :

La Structure de Tabular
Nous avons dit qu’un Tabular est « comme » une liste d’associations. Et en effet, si vous appliquez Normal dessus, c’est exactement ce que vous obtiendrez :

Mais en interne, Tabular est stocké de manière beaucoup plus compacte et efficace. Et il est utile d’en savoir un peu plus à ce sujet, afin de pouvoir manipuler des objets Tabular sans avoir besoin de les « démonter » en listes ou associations, par exemple. Voici notre Tabular d’exemple de base :

Que se passe-t-il si l’on extrait une ligne ? Eh bien, on obtient un objet TabularRow:

Si l’on applique Normal, on obtient une association :

Voici ce qui se passe si, à la place, on extrait une colonne :
Cette fois, Normal renvoie une liste :

On peut créer une TabularColumn à partir d’une liste :

Nous pouvons maintenant utiliser InsertColumns pour insérer une colonne symbolique comme celle-ci dans un Tabular existant (le « b » spécifie à InsertColumns d’insérer la nouvelle colonne après la colonne “b”) :

Mais qu’est-ce qu’un Tabular en interne, réellement ? Regardons l’exemple de plus près :

TabularStructure nous donne un résumé de la structure interne dans ce cas :

La première chose à remarquer, c’est que tout est exprimé en termes de colonnes, ce qui reflète le fait que Tabular est fondamentalement une structure orientée colonne. Et ce qui rend Tabular si efficace, c’est notamment que, dans chaque colonne, tout est uniforme : toutes les valeurs sont du même type de données. De plus, pour les éléments comme les quantités ou les dates, les données sont factorisées, ce qui signifie que ce qui est réellement stocké en interne dans la colonne n’est qu’une liste de nombres, accompagnée d’une seule copie des « métadonnées » expliquant comment les interpréter.
Et oui, tout cela a un impact important. Par exemple, voici la taille en octets de notre Tabular des arbres de New York mentionné plus haut:

Mais si nous le transformons en une liste d’associations en utilisant Normal, le résultat est environ 14 fois plus volumineux :

D’accord, mais que sont exactement ces « types de colonnes » dans la structure tabulaire ? ColumnTypes fournit une liste de ces types :

Ce sont des types de bas niveau, du genre utilisé par le compilateur du Wolfram Language. Et ce qui est intéressant en les connaissant, c’est qu’ils nous indiquent immédiatement quelles opérations peuvent être effectuées sur une colonne donnée. Cela est utile à la fois pour des traitements de bas niveau, mais aussi pour savoir, par exemple, quel type de visualisation est possible.
Lorsque Import importe des données à partir d’un fichier CSV, par exemple, il essaie de deviner le type de chaque colonne. Mais parfois (comme mentionné plus haut), vous voudrez forcer une colonne à adopter un autre type, en spécifiant le type de destination à l’aide d’une description de type du Wolfram Language. Par exemple, ceci force la colonne « b » à être un nombre réel 32 bits, et la colonne « c » à représenter des unités en mètres :

Au passage, lorsqu’un Tabular est affiché dans un notebook, les en-têtes de colonnes indiquent les types de données contenus dans les colonnes correspondantes. Ainsi, dans ce cas, une petite icône apparaît dans la première colonne pour indiquer qu’elle contient des chaînes de caractères. Les nombres et les dates « se montrent » simplement tels quels. Les quantités affichent leurs unités. Et les expressions symboliques générales (comme la colonne « f » ici) sont indiquées par une icône
. (Si vous survolez un en-tête de colonne, vous obtiendrez plus de détails sur le type de données.)
La prochaine chose à aborder est la gestion des données manquantes. Tabular considère toujours que les colonnes sont d’un type uniforme, mais il conserve en parallèle une carte globale indiquant où les valeurs sont manquantes. Si vous extrayez une colonne, vous verrez apparaître un symbole Missing:

Mais si vous effectuez des opérations directement sur la colonne tabulaire, elle se comportera simplement comme si les données manquantes étaient… effectivement absentes :

Au fait, lorsque vous importez des données « brutes », Import tentera automatiquement d’inférer le type approprié pour chaque colonne. Il sait gérer les anomalies courantes dans les données d’entrée, comme NaN ou null dans une colonne de nombres. Mais s’il y a d’autres valeurs étranges — comme, par exemple, notfound au milieu d’une colonne numérique — vous pouvez indiquer à Import de les traiter comme des données manquantes ordinaires, en les spécifiant via l’option MissingValuePattern.
Il reste quelques subtilités à aborder concernant la structure des objets Tabular. La première est la notion de clés étendues (extended keys). Supposons que nous ayons le Tabular suivant :

Nous pouvons « pivoter cela en colonnes » de manière à ce que les valeurs x et y deviennent des en-têtes de colonnes, mais placés « sous » l’en-tête global « value » :

Mais quelle est la structure de ce Tabular? Nous pouvons utiliser ColumnKeys pour le découvrir :

Vous pouvez maintenant utiliser ces clés étendues comme indices pour accéder aux données du Tabular :

Dans ce cas particulier, comme les « sous-clés » « x » et « y » sont uniques, nous pouvons simplement les utiliser directement, sans inclure l’autre partie de la clé étendue :

Notre dernière subtilité (pour le moment) y est en quelque sorte liée. Elle concerne les colonnes de clé (key columns). Normalement, pour désigner une ligne dans un objet Tabular, on utilise simplement sa position. Mais si les valeurs d’une colonne particulière sont uniques, alors on peut les utiliser pour identifier directement une ligne. Prenons ce Tabular en exemple :

La colonne fruit présente la particularité que chaque valeur n’y apparaît qu’une seule fois — nous pouvons donc créer un Tabular en utilisant cette colonne comme colonne de clé (key column) :

Remarquez que les numéros de lignes ont maintenant disparu, et que la colonne de clé est indiquée par un fond gris. Dans ce Tabular, vous pouvez alors référencer une ligne particulière en utilisant par exemple RowKey :

De manière équivalente, vous pouvez aussi utiliser une association avec le nom de la colonne :

Que se passe-t-il si les valeurs d’une seule colonne ne suffisent pas à identifier une ligne de manière unique, mais qu’un ensemble de colonnes le permet ? (Dans un exemple réel, imaginez qu’une colonne contient les prénoms, une autre les noms de famille, et une autre les dates de naissance.) Eh bien, dans ce cas, vous pouvez désigner l’ensemble de ces colonnes comme colonnes de clé (key columns) :

Et une fois que cela est fait, vous pouvez référencer une ligne en fournissant les valeurs correspondant à toutes les colonnes de clé :

Tabular partout
Tabular offre une nouvelle manière importante de représenter des données structurées dans le Wolfram Language. C’est un outil puissant en soi, mais ce qui le rend encore plus puissant, c’est son intégration avec toutes les autres fonctionnalités du Wolfram Language. De nombreuses fonctions fonctionnent immédiatement avec Tabular, mais dans la version 14.2, des centaines de fonctions ont été améliorées pour tirer parti des caractéristiques particulières de Tabular.
Le plus souvent, cela permet d’opérer directement sur les colonnes d’un Tabular. Par exemple, étant donné le Tabular suivant :

Nous pouvons immédiatement créer une visualisation basée sur deux des colonnes :

Si l’une des colonnes contient des données catégorielles, celles-ci seront automatiquement reconnues, et la visualisation sera adaptée en conséquence :

Un autre domaine où Tabular peut être utilisé immédiatement est l’apprentissage automatique (machine learning). Par exemple, on peut créer une fonction de classification qui tentera de déterminer l’espèce d’un manchot à partir d’autres données le concernant :

Nous pouvons maintenant utiliser cette fonction de classification pour prédire l’espèce d’un manchot à partir d’autres données le concernant :

Nous pouvons également prendre l’ensemble du Tabular et créer un graphique de l’espace des caractéristiques (feature space plot), en étiquetant chaque point selon l’espèce :

Ou bien, nous pourrions « apprendre la distribution des manchots possibles »

et générer aléatoirement 3 « manchots fictifs » à partir de cette distribution :

Algèbre avec des tableaux symboliques
L’une des innovations majeures de la Version 14.1 a été l’introduction des tableaux symboliques — et la possibilité de créer des expressions impliquant des variables vecteurs, matrices et tableaux, ainsi que de calculer leurs dérivées. Dans la version 14.2, nous allons encore plus loin dans le calcul avec des tableaux symboliques : pour la première fois, nous automatisons de manière systématique ce qui était auparavant un processus manuel — faire de l’algèbre avec des tableaux symboliques et simplifier les expressions qui les impliquent.
Commençons par parler de ArrayExpand. Notre fonction bien établie Expand se contente de développer des multiplications ordinaires, en pratique entre scalaires — donc, dans ce cas, elle ne fait rien :

Mais dans la version 14.2, nous avons aussi ArrayExpand, qui effectuera le développement :

ArrayExpand gère de nombreuses généralisations de la multiplication qui ne sont pas commutatives :

Dans un exemple comme celui-ci, nous n’avons vraiment pas besoin de connaître quoi que ce soit sur a et b. Mais parfois, il est impossible d’effectuer le développement sans, par exemple, connaître leurs dimensions. Une façon de spécifier ces dimensions est d’ajouter une condition dans ArrayExpand:

Une autre possibilité consiste à utiliser une variable de tableau symbolique explicite :

En plus de développer les produits généralisés avec ArrayExpand, la version 14.2 prend également en charge la simplification générale des expressions de tableaux symboliques :

La fonction ArraySimplify permet de simplifier spécifiquement les tableaux symboliques, tout en laissant les autres parties des expressions inchangées. La version 14.2 prend en charge de nombreux types de simplifications de tableaux:


Nous pourrions effectuer ces simplifications sans rien savoir des dimensions de a et b. Mais parfois, il est impossible d’aller aussi loin sans connaître ces dimensions. Par exemple, si nous ne connaissons pas les dimensions, nous obtenons :

Mais avec les dimensions, nous pouvons simplifier explicitement cela en une matrice identité n×n:

ArraySimplify peut également tenir compte des symétries des tableaux. Par exemple, définissons une matrice symétrique symbolique:

Et maintenant, ArraySimplify peut immédiatement résoudre cela:

La capacité d’effectuer des opérations algébriques sur des tableaux entiers sous forme symbolique est très puissante. Mais il est parfois tout aussi important d’examiner les composantes individuelles des tableaux. Et dans la version 14.2, nous avons ajouté ComponentExpand pour permettre d’obtenir les composantes des tableaux sous forme symbolique.
Ainsi, par exemple, ceci prend un vecteur à 2 composantes et le développe en une liste explicite avec deux composantes symboliques:

En arrière-plan, ces composantes sont représentées à l’aide de Indexed:

Voici le déterminant d’une matrice 3×3, exprimé en termes de composantes symboliques:

Et voici une puissance de matrice:

Étant donnés deux vecteurs 3D et
, nous pouvons également, par exemple, former le produit vectoriel:

et nous pouvons ensuite le multiplier scalairement par une matrice inverse:

Ajustements linguistiques
En tant qu’utilisateur quotidien du Wolfram Language, je suis très satisfait de la facilité avec laquelle je peux traduire des idées computationnelles en code. Mais plus nous facilitons cette tâche, plus nous découvrons de nouveaux aspects où nous pouvons encore améliorer le langage. Et dans la version 14.2 — comme dans toutes les versions précédentes — nous avons ajouté un certain nombre de « réglages linguistiques ».
Une fonction simple — dont l’utilité devient particulièrement évidente avec Tabular — est Discard. On peut la voir comme un complément de Select : elle élimine les éléments selon le critère que vous spécifiez :

Et en plus d’ajouter Discard, nous avons également amélioré Select. Habituellement, Select renvoie simplement la liste des éléments qu’elle sélectionne. Mais dans la version 14.2, vous pouvez spécifier d’autres résultats. Ici, nous demandons l’« indice » (c’est-à-dire la position) des éléments que NumberQ sélectionne:

Quelque chose qui peut être utile pour traiter de très grandes quantités de données est d’obtenir une structure de données vecteur de bits à partir de Select (et Discard), qui fournit un masque binaire indiquant quels éléments sont sélectionnés ou non:

En parlant de Tabular, nous avons déjà mentionné MissingFallback. Une autre fonction liée à la robustesse du code et à la gestion des erreurs est la nouvelle fonction Failsafe. Supposons que vous ayez une liste contenant certains éléments « en échec ». Si vous appliquez une fonction f à cette liste, elle s’appliquera aux éléments en échec comme à tous les autres :

Mais il est tout à fait possible que f ne soit pas conçue pour gérer ce type d’entrées défaillantes. C’est là qu’intervient Failsafe. En effet, Failsafe[f][x] est défini pour renvoyer f[x] si x n’est pas une défaillance, et pour retourner simplement la défaillance sinon. Ainsi, nous pouvons maintenant appliquer f à notre liste en toute sécurité, sachant qu’elle ne recevra jamais d’entrée défaillante :

En parlant de cas d’erreurs délicats, une autre nouvelle fonction de la version 14.2 est HoldCompleteForm. HoldForm vous permet d’afficher une expression sans en effectuer l’évaluation ordinaire. Mais — comme Hold — elle autorise néanmoins certaines transformations. HoldCompleteForm — comme HoldComplete — empêche toutes ces transformations. Ainsi, alors que HoldForm devient un peu confus ici lorsque la séquence « se résout »

HoldCompleteForm bloque complètement et affiche la séquence telle quelle:

Une autre amélioration apportée dans la version 14.2 concerne Counts. Il m’arrive souvent de vouloir compter les éléments d’une liste, y compris obtenir 0 lorsqu’un certain élément est absent. Par défaut, Counts ne compte que les éléments présents :

Mais dans la version 14.2, nous avons ajouté un second argument qui vous permet de fournir une liste complète de tous les éléments que vous souhaitez compter — même s’ils sont absents de la liste :

Pour finir avec un exemple d’ajustement linguistique dans la version 14.2, je vais mentionner AssociationComap. Dans la version 14.0, nous avions introduit Comap comme un analogue « co- » (comme dans « co-foncteur », etc.) de Map:

Dans la version 14.2, nous introduisons AssociationComap — la version « co- » de AssociationMap:

Considérez cela comme un moyen pratique de créer des tableaux étiquetés d’éléments, comme dans:

Éclaircir nos couleurs ; Se refaire une beauté pour 2025
En 2014 — pour la Version 10.0 — nous avons procédé à une refonte majeure des couleurs par défaut pour toutes nos fonctions graphiques et de visualisation, en proposant ce que nous pensions être une bonne solution. (Et, comme nous l’avons remarqué tout récemment, de manière un peu surprenante, il s’est avéré que dans les années qui ont suivi, beaucoup de bibliothèques graphiques et de visualisation semblaient avoir copié ce que nous avions fait !) Eh bien, une décennie s’est maintenant écoulée, les attentes visuelles (et les technologies d’affichage) ont évolué, et nous avons décidé qu’il était temps de refaire nos couleurs pour 2025.
Voici à quoi ressemblait un graphique typique dans les Versions 10.0 à 14.1:

Et voici le même graphique dans la version 14.2:

Par conception, il reste entièrement reconnaissable, mais avec un petit plus de peps.
Avec plus de courbes, il y a plus de couleurs. Voici l’ancienne version:

Et voici la nouvelle version:

Les histogrammes sont également plus lumineux. L’ancien :

Et le nouveau:

Voici la comparaison entre les anciennes couleurs (« 2014 ») et les nouvelles (« 2025 »):

C’est subtil, mais cela fait une différence. Je dois dire que, de plus en plus ces dernières années, j’avais l’impression de devoir ajuster les couleurs dans presque toutes les images Wolfram Language que je publiais. Mais je suis heureux de dire qu’avec les nouvelles couleurs, cette nécessité a disparu — et je peux simplement utiliser à nouveau nos couleurs par défaut!
LLM Streamlining & Streaming
Nous avons introduit pour la première fois un accès programmatique aux modèles de langage (LLM) dans Wolfram Language au milieu de l’année 2023, avec des fonctions comme LLMFunction et LLMSynthesize. À cette époque, ces fonctions nécessitaient un accès à des services LLM externes. Mais avec la sortie le mois dernier de LLM Kit (ainsi que Wolfram Notebook Assistant), nous avons rendu ces fonctions disponibles de manière transparente pour tous ceux qui disposent d’un abonnement Notebook Assistant + LLM Kit. Une fois votre abonnement activé, vous pouvez utiliser les fonctions LLM programmatiques partout dans la version 14.2, sans configuration supplémentaire.
Il y a également deux nouvelles fonctions : LLMSynthesizeSubmit et ChatSubmit. Toutes deux permettent d’obtenir des résultats incrémentiels des LLM (et oui, c’est important, du moins pour l’instant, car les LLM peuvent être assez lents). À l’instar de CloudSubmit et URLSubmit, LLMSynthesizeSubmit et ChatSubmit sont des fonctions asynchrones : vous les appelez pour lancer un processus qui invoquera une fonction gestionnaire appropriée chaque fois qu’un événement spécifié se produit.
LLMSynthesizeSubmit et ChatSubmit prennent tous deux en charge toute une variété d’événements. Un exemple est « ContentChunkReceived » : un événement qui se produit lorsqu’un morceau de contenu est reçu du modèle de langage (LLM).
Voici comment on peut utiliser cela:

LLMSynthesizeSubmit retourne un TaskObject, mais commence ensuite à synthétiser du texte en réponse à l’invite que vous avez donnée, appelant la fonction gestionnaire que vous avez spécifiée à chaque fois qu’un morceau de texte arrive. Après quelques instants, le LLM aura terminé son processus de synthèse de texte, et si vous demandez la valeur de c, vous verrez chacun des morceaux qu’il a produits:

Essayons cela à nouveau, mais cette fois en configurant un affichage dynamique pour une chaîne s, puis en exécutant LLMSynthesizeSubmit pour accumuler le texte synthétisé dans cette chaîne:
ChatSubmit est l’analogue asynchrone de ChatEvaluate — et vous pouvez l’utiliser pour créer une expérience de chat complète, dans laquelle le contenu arrive en continu dans votre notebook dès que le LLM (ou les outils appelés par le LLM) le génèrent.
Rationalisation du calcul parallèle : Lancez toutes les machines!
Depuis près de 20 ans, nous disposons dans le Wolfram Language d’une fonctionnalité fluide pour effectuer des calculs parallèles, à l’aide de fonctions comme ParallelMap, ParallelTable et Parallelize. Le calcul parallèle peut s’effectuer sur plusieurs cœurs d’une seule machine, ou sur de nombreuses machines connectées à un réseau. (Et, par exemple, dans ma configuration actuelle, j’ai 7 machines avec un total de 204 cœurs.)
Au cours des dernières années, en réponse notamment au nombre croissant de cœurs généralement disponibles sur les machines individuelles, nous avons progressivement rationalisé la manière dont le calcul parallèle est mis en place. Et dans la version 14.2, nous avons — oui — parallélisé le lancement du calcul parallèle. Ce qui signifie, par exemple, que mes 7 machines démarrent désormais toutes leurs noyaux parallèles en parallèle — de sorte que l’ensemble du processus s’achève maintenant en quelques secondes, au lieu de prendre potentiellement plusieurs minutes comme auparavant:

Une autre nouveauté pour le calcul parallèle dans la version 14.2 est la possibilité de paralléliser automatiquement sur plusieurs variables dans ParallelTable. ParallelTable disposait déjà de divers algorithmes pour optimiser la répartition des calculs entre les différents noyaux. Cette capacité a maintenant été étendue pour pouvoir gérer plusieurs variables :

En tant qu’utilisateur régulier du Wolfram Language pour des calculs à grande échelle, il est difficile de surestimer à quel point les capacités de calcul parallèle ont été essentielles pour moi. En général, je commence par concevoir un calcul avec Map, Table, etc. Puis, lorsque je suis prêt à exécuter la version complète, je remplace simplement par ParallelMap, ParallelTable, etc. Et c’est impressionnant de voir à quel point une augmentation de vitesse par un facteur 200 peut faire la différence (à condition, bien sûr, que le calcul ne nécessite pas trop de communication entre les processus).
(À propos, en parlant de surcharge de communication, deux nouvelles fonctions dans la version 14.2 sont ParallelSelect et ParallelCases, qui permettent de sélectionner et de rechercher des cas dans des listes en parallèle, en réduisant la surcharge de communication grâce à l’envoi au noyau principal uniquement des résultats finaux. Cette fonctionnalité était en réalité déjà disponible depuis un certain temps via Parallelize [ … Select [ … ] … ], etc., mais elle est désormais rationalisée dans la version 14.2.)
Suivez ce ____! Suivi dans les vidéos
Imaginons que nous ayons une vidéo, par exemple de personnes marchant dans une gare. Nous avions depuis quelque temps la capacité de prendre une image fixe de cette vidéo et d’y détecter les personnes. Mais dans la version 14.2, nous avons quelque chose de nouveau : la capacité de suivre des objets se déplaçant d’une image à l’autre dans la vidéo.
Commençons par une vidéo:

Nous pourrions prendre une image individuelle et trouver les zones délimitées (bounding boxes). Mais à partir de la version 14.2, nous pouvons simplement appliquer ImageBoundingBoxes à toute la vidéo d’un coup :

Ensuite, nous pouvons utiliser les données des zones délimitées pour mettre en évidence les personnes dans la vidéo — en utilisant la nouvelle fonction HighlightVideo:

Mais cela indique seulement séparément où se trouvent les personnes dans chaque image ; cela ne les relie pas d’une image à l’autre. Dans la version 14.2, nous avons ajouté VideoObjectTracking pour suivre les objets entre les images :

Maintenant, si nous utilisons HighlightVideo, les différents objets seront annotés avec des couleurs différentes:

Cela repère tous les objets uniques identifiés au cours de la vidéo, et les compte:

« Où est le chien? », pourriez-vous demander. Il n’y reste certainement pas longtemps:

Et si nous cherchons la première image où il est censé apparaître, il semble effectivement que ce qui est probablement une personne en bas à droite ait été confondu avec un chien :

Et oui, c’est bien ce que le système a pris pour un chien:

Théorie des jeux
« Qu’en est-il de la théorie des jeux ? », demande-t-on depuis longtemps. Et oui, beaucoup de travaux de théorie des jeux ont été réalisés avec le Wolfram Language, ainsi que de nombreux packages dédiés à certains aspects. Mais dans la version 14.2, nous introduisons enfin des fonctions système intégrées pour faire de la théorie des jeux (à la fois des jeux matriciels et des jeux en arbre).
Voici comment nous spécifions un jeu matriciel à deux joueurs à somme nulle:

Cela définit les gains lorsque chaque joueur effectue chaque action. Nous pouvons représenter cela sous forme de jeu de données (dataset):

Une autre possibilité est de « visualiser le jeu » avec MatrixGamePlot:

D’accord, alors comment peut-on « résoudre » ce jeu ? En d’autres termes, quelle action chaque joueur doit-il prendre, avec quelle probabilité, pour maximiser son gain moyen sur de nombreuses parties du jeu ? (On suppose que, à chaque partie, les joueurs choisissent simultanément et indépendamment leurs actions.) Une « solution » qui maximise les gains attendus pour tous les joueurs s’appelle un équilibre de Nash. (Petite anecdote historique : John Nash a été un utilisateur de longue date de Mathematica et de ce qui est aujourd’hui le Wolfram Language — bien des années après avoir inventé le concept d’équilibre de Nash.) Eh bien, désormais dans la version 14.2, FindMatrixGameStrategies calcule les stratégies optimales (aussi appelées équilibres de Nash) pour les jeux matriciels:

Ce résultat signifie que, pour ce jeu, le joueur 1 doit jouer l’action 1 avec une probabilité de et l’action 2 avec une probabilité de
, tandis que le joueur 2 doit jouer ces actions avec des probabilités de
et
. Mais quels sont leurs gains espérés ? MatrixGamePayoff permet de les calculer:

Il peut devenir assez difficile de suivre les différents cas dans un jeu, c’est pourquoi MatrixGame vous permet d’attribuer les étiquettes que vous souhaitez aux joueurs et aux actions:

Ces étiquettes sont ensuite utilisées dans les visualisations:

Ce que nous venons de montrer est en réalité un exemple classique de jeu — le «dilemme du prisonnier». Dans le Wolfram Language, nous avons désormais GameTheoryData, un référentiel contenant une cinquantaine de jeux standards. En voici un, spécifié pour avoir 4 joueurs:

Et résoudre ce jeu est moins trivial, mais voici le résultat — avec 27 solutions distinctes:

Et oui, les visualisations continuent de fonctionner, même lorsqu’il y a plus de joueurs (ici, nous affichons le cas avec 5 joueurs, en indiquant la 50th solution du jeu):

Cela vaut peut-être la peine de mentionner que la façon dont nous résolvons ce type de jeux repose sur nos capacités les plus récentes de résolution d’équations polynomiales — et non seulement nous parvenons de manière systématique à trouver tous les équilibres de Nash possibles (et pas simplement un point fixe), mais nous sommes également capables d’obtenir des résultats exacts:

En plus des jeux matriciels, qui modélisent des jeux où les joueurs choisissent simultanément leurs actions une seule fois, nous prenons également en charge les jeux en arbre, dans lesquels les joueurs jouent à tour de rôle, ce qui génère un arbre des issues possibles, chacune se terminant par un gain spécifié pour chaque joueur. Voici un exemple d’un jeu en arbre très simple:

Nous pouvons obtenir au moins une solution pour ce jeu — décrite par une structure imbriquée qui donne les probabilités optimales pour chaque action de chaque joueur à chaque tour:

Les choses peuvent devenir plus complexes avec les jeux en arbre. Voici un exemple — dans lequel certains joueurs ne savent parfois pas quelles branches ont été empruntées (comme indiqué par les états reliés par des lignes pointillées):

Ce que nous proposons dans la version 14.2 représente une couverture assez complète des concepts de base que l’on trouve dans un cours introductif classique de théorie des jeux. Mais maintenant, à la manière typique du Wolfram Language, tout cela est calculable et extensible — vous pouvez donc étudier des jeux plus réalistes, et rapidement faire de nombreux exemples pour développer votre intuition.
Jusqu’à présent, nous nous sommes concentrés sur la « théorie des jeux classique », notamment avec la particularité (pertinente pour de nombreuses applications actuelles) que tous les nœuds d’action résultent d’une séquence différente d’actions. Cependant, des jeux comme le morpion (que j’ai récemment étudié en utilisant des graphes multi-chemins) peuvent être simplifiés en fusionnant les nœuds d’action équivalents. Plusieurs séquences d’actions peuvent mener au même jeu de morpion, comme c’est souvent le cas pour les jeux itérés. Ces structures de graphes ne s’intègrent pas dans le type d’arbres classiques de théorie des jeux que nous avons introduits dans la version 14.2 — bien que (comme je pense que mes propres travaux le montrent) elles soient particulièrement adaptées à l’analyse avec le Wolfram Language.
Calcul des syzygies et autres avancées en astronomie
Il y a beaucoup de « coïncidences » en astronomie — des situations où des objets s’alignent d’une certaine manière. Les éclipses en sont un exemple. Mais il y en a beaucoup d’autres. Et dans la version 14.2, il existe désormais une fonction générale FindAstroEvent pour trouver ces « coïncidences », appelées techniquement syzygies (« sizz-ee-gees »), ainsi que d’autres « configurations particulières » d’objets astronomiques.
Un exemple simple est l’équinoxe de septembre (équinoxe d’automne):

En gros, c’est le moment où le jour et la nuit ont une durée égale. Plus précisément, c’est lorsque le soleil se trouve à l’une des deux positions dans le ciel où le plan de l’écliptique (c’est-à-dire le plan orbital de la Terre autour du Soleil) croise l’équateur céleste (c’est-à-dire la projection de l’équateur terrestre) — comme on peut le voir ici (l’écliptique est la ligne jaune ; l’équateur céleste la ligne bleue):

Comme autre exemple, cherchons la prochaine fois, au cours du siècle à venir, où Jupiter et Saturne seront les plus proches dans le ciel:

Ils seront suffisamment proches pour que l’on puisse voir leurs lunes ensemble:

Il existe un nombre incroyable de configurations astronomiques auxquelles on a historiquement attribué des noms spécifiques. Il y a les équinoxes, les solstices, les équiluxes, les culminations, les conjonctions, les oppositions, les quadratures — ainsi que les périapses et apoapses (spécialisés en périgée, périhélie, périarion, périjove, périkrone, périuranion, périposeïdeum, etc.). Dans la version 14.2, nous prenons en charge toutes ces configurations.
Par exemple, ceci donne la prochaine fois où Triton sera le plus proche de Neptune :

Un exemple célèbre concerne le périhélie (le point de l’orbite le plus proche du Soleil) de Mercure. Calculons la position de Mercure (vue depuis le Soleil) à chacun de ses périhélies au cours des deux premières décennies du XIXᵉ siècle :

On observe une « avance » systématique (accompagnée de quelques oscillations) :

Passons maintenant au calcul quantitatif de cette avance. Nous commençons par trouver les dates des premiers périhélies en 1800 et en 1900 :

Maintenant, calculons la séparation angulaire entre les positions de Mercure à ces dates :

Puis divisez cela par la différence de temps :

et convertissez les unités :

Célèbrement, 43 secondes d’arc par siècle de ce phénomène résultent des écarts par rapport à la loi de la gravitation inverse au carré, introduits par la relativité générale — et bien sûr, pris en compte par notre système de calcul astronomique. (Le reste de l’avance est dû aux effets gravitationnels traditionnels de Vénus, Jupiter, la Terre, etc.)
Les PDE sont désormais également destinés aux systèmes magnétiques
Il y a plus de quinze ans, nous avons pris l’engagement de faire du Wolfram Language un environnement complet de modélisation d’équations aux dérivées partielles (EDP). Bien sûr, cela a été facilité par le fait que nous pouvions nous appuyer sur toutes les autres capacités du Wolfram Language — et ce que nous avons pu produire est infiniment plus précieux grâce à la synergie avec le reste du système. Mais au fil des années, avec beaucoup d’efforts, nous avons progressivement développé des capacités symboliques de modélisation d’EDP dans tous les domaines standards. Et aujourd’hui, je pense qu’on peut dire sans exagération que nous sommes capables de traiter — à une échelle industrielle — une grande partie de la modélisation d’EDP qui se présente dans des situations réelles.
Mais il existe toujours d’autres cas pour lesquels nous pouvons intégrer des capacités, et dans la version 14.2, nous ajoutons des primitives intégrées pour la modélisation des champs magnétiques statiques et quasi-statiques. Par exemple, voici comment nous pouvons désormais modéliser un aimant en forme de sablier. Cela définit les conditions aux limites — puis résout les équations pour le potentiel scalaire magnétique:

Nous pouvons ensuite prendre ce résultat et, par exemple, tracer immédiatement les lignes de champ magnétique qu’il implique:

La version 14.2 ajoute également les primitives permettant de traiter les courants électriques à variation lente, ainsi que les champs magnétiques qu’ils génèrent. Tout cela s’intègre immédiatement avec nos autres domaines de modélisation tels que le transfert de chaleur, la dynamique des fluides, l’acoustique, etc.
Il y a beaucoup à dire sur la modélisation des EDP et ses applications, et dans la version 14.2, nous avons ajouté plus de 200 pages de documentation supplémentaire de style manuel scolaire sur la modélisation des EDP, incluant quelques exemples de niveau recherche.
Nouvelles fonctionnalités dans les graphiques, la géométrie et les graphiques
Les graphiques ont toujours été un point fort du Wolfram Language, et au cours de la dernière décennie, nous avons également développé des capacités très solides en géométrie algorithmique. La version 14.2 ajoute un peu plus de « cerise sur le gâteau », notamment en reliant les graphiques à la géométrie, et la géométrie à d’autres parties du système.
À titre d’exemple, la version 14.2 ajoute des capacités géométriques à davantage de ce qui étaient auparavant de simples primitives graphiques. Par exemple, voici une région géométrique formée en remplissant une courbe de Bézier:

Et nous pouvons désormais effectuer toutes nos opérations habituelles de géométrie algorithmique dessus:

Quelque chose comme ceci fonctionne désormais aussi:

Autre nouveauté dans la version 14.2 : MoleculeMesh, qui permet de créer une géométrie calculable à partir de structures moléculaires. Voici une représentation graphique d’une molécule:

Et voici maintenant un maillage géométrique correspondant à la molécule:

Nous pouvons ensuite effectuer des opérations de géométrie algorithmique sur ce maillage:


Une autre nouvelle fonctionnalité de la version 14.2 est une méthode supplémentaire pour le dessin de graphes qui peut exploiter les symétries. Si vous créez un graphe en couches à partir d’une grille symétrique, il ne s’affichera pas immédiatement de manière symétrique:

Mais avec la nouvelle disposition de graphe « SymmetricLayeredEmbedding », ce sera le cas:

Mises au point de l’interface utilisateur
Créer une excellente interface utilisateur est toujours une histoire d’améliorations constantes, et cela fait maintenant près de quarante ans que nous peaufinons l’interface notebook. Dans la version 14.2, plusieurs retouches notables ont été ajoutées. L’une d’elles concerne l’autocomplétion des valeurs d’options.
Depuis longtemps, nous affichons des suggestions de complétion pour les options qui ont un ensemble discret de valeurs courantes bien définies (comme All, Automatic, etc.). Dans la version 14.2, nous ajoutons des « complétions avec modèle » qui indiquent la structure attendue des paramètres, puis vous permettent d’utiliser la touche Tab pour compléter les valeurs spécifiques. Depuis toutes ces années, l’un des endroits où je me rends presque systématiquement dans la documentation est la rubrique des paramètres pour FrameLabel. Mais désormais, l’autocomplétion me montre immédiatement la structure de ces paramètres:
Toujours concernant l’autocomplétion, nous avons ajouté la capacité de compléter automatiquement les noms de contextes, les alias de contextes, ainsi que les symboles contenant des contextes. Et dans tous les cas, l’autocomplétion est dite « floue », c’est-à-dire qu’elle se déclenche non seulement sur les caractères en début de nom, mais aussi sur ceux présents n’importe où dans le nom — ce qui signifie que vous pouvez simplement taper quelques lettres d’un symbole, et les contextes pertinents s’afficheront dans les suggestions.
Une autre petite amélioration apportée dans la version 14.2 est la possibilité de glisser-déposer des images d’un notebook vers un autre notebook, ou même vers n’importe quelle autre application capable de recevoir des images glissées. Il était déjà possible de faire glisser des images depuis d’autres applications vers les notebooks, mais désormais, l’inverse est également possible.
Une autre nouveauté — pour l’instant spécifique à macOS — est la prise en charge améliorée de l’aperçu des icônes (ainsi que de Quick Look). Désormais, si vous avez un dossier rempli de notebooks et que vous sélectionnez l’affichage Icône, vous verrez une petite représentation de chaque notebook sous forme d’icône reflétant son contenu :
En coulisses, la version 14.2 introduit également certains développements d’infrastructure qui permettront d’ajouter des fonctionnalités importantes dans les versions suivantes. Certains de ces développements concernent la prise en charge généralisée du mode sombre. (Oui, on pourrait croire au départ que le mode sombre est quelque chose de trivial, mais dès qu’on commence à penser à tous les éléments graphiques et d’interface qui impliquent des couleurs, on comprend que ce ne l’est pas. Par exemple, après des efforts considérables, nous avons récemment publié un mode sombre pour Wolfram|Alpha.)
Ainsi, par exemple, dans la version 14.2, vous trouverez le nouveau symbole LightDarkSwitched, qui fait partie du mécanisme permettant de définir des styles qui basculent automatiquement entre les modes clair et sombre. Et oui, il existe une option de style appelée LightDark qui permet de basculer les notebooks entre les modes — et qui est au moins prise en charge de manière expérimentale.
Lié au mode clair/sombre, on trouve également la notion de couleurs de thème : des couleurs définies de manière symbolique et pouvant être basculées ensemble. Et oui, un symbole expérimental nommé ThemeColor y est associé. Mais le déploiement complet de tout ce mécanisme n’est prévu que pour la prochaine version.
Les débuts du passage au natif sur les GPU
De nombreuses fonctionnalités essentielles du Wolfram Language utilisent automatiquement le GPU lorsqu’il est disponible. Et cela fait déjà 15 ans que nous avons introduit des primitives pour la programmation GPU bas niveau. Mais dans la version 14.2, nous commençons à rendre les capacités GPU plus facilement accessibles afin d’optimiser l’utilisation générale du Wolfram Language.
L’élément clé introduit est GPUArray, qui représente un tableau de données qui sera (si possible) stocké de manière à être directement et immédiatement accessible par votre GPU. (Sur certains systèmes, il sera stocké dans une mémoire GPU distincte ; sur d’autres, comme les Mac modernes, il sera stocké en mémoire partagée, de manière à être accessible directement par le GPU.)
Dans la version 14.2, nous prenons en charge un ensemble initial d’opérations pouvant être effectuées directement sur les tableaux GPU (GPUArray). Les opérations disponibles varient légèrement selon le type de GPU utilisé.
À terme, nous prévoyons d’exploiter ou de créer de nombreuses bibliothèques GPU supplémentaires, afin d’étendre l’ensemble des opérations réalisables sur les GPUArray.
Voici un vecteur aléatoire de dix millions d’éléments stocké sous forme de GPUArray:

Le GPU du Mac sur lequel j’écris ceci prend en charge les opérations nécessaires pour effectuer ce calcul entièrement sur le GPU, renvoyant un résultat sous forme de GPUArray:

Voici le temps d’exécution:

Et voici le résultat correspondant en mode ordinaire (CPU):

Dans ce cas, le résultat avec GPUArray est environ deux fois plus rapide. Le gain varie en fonction des opérations effectuées et du matériel utilisé. Jusqu’à présent, les plus grandes accélérations que j’ai observées sont d’environ 10 fois. Mais à mesure que nous développons davantage de bibliothèques GPU, je m’attends à ce que ce facteur augmente — en particulier lorsque les calculs se déroulent principalement « à l’intérieur du GPU », avec peu d’accès mémoire.
Au fait, si vous parsemez votre code de GPUArray, cela n’affectera normalement jamais les résultats obtenus — car les opérations s’exécutent toujours par défaut sur le CPU si elles ne sont pas prises en charge par votre GPU. (En général, GPUArray accélérera les calculs, mais s’il y a trop de « ratés GPU », toutes les tentatives de transfert de données peuvent en fait ralentir le processus.) Il faut aussi garder à l’esprit que le calcul GPU n’est pas encore du tout standardisé ni uniforme. Parfois, seul le support des vecteurs est assuré, parfois aussi celui des matrices — et différents types de données avec différentes précisions numériques sont supportés selon les cas.
Et encore plus…
En plus de tout ce que nous avons déjà abordé ici, la version 14.2 comporte également toute une série de « petites » nouvelles fonctionnalités. Et même si elles peuvent sembler « petites » comparées aux autres nouveautés, elles seront très importantes si vous avez justement besoin de ces fonctionnalités.
Par exemple, il y a MidDate, qui calcule le point médian entre des dates :

Et comme presque tout ce qui concerne les dates, MidDate est rempli de subtilités. Ici, il calcule la semaine située aux deux tiers de cette année:

En mathématiques, des fonctions comme DSolve et SurfaceIntegrate peuvent désormais gérer des variables de tableaux symboliques:

SumConvergence permet désormais de spécifier la plage de sommation, et peut fournir des conditions qui en dépendent:

Un petit confort que, oui, j’ai demandé : DigitCount permet maintenant de spécifier combien de chiffres au total on veut supposer que le nombre a, afin qu’il compte correctement les zéros en tête:

En parlant de commodités, pour des fonctions comme MaximalBy et TakeLargest, nous avons ajouté un nouvel argument qui indique comment trier les éléments pour déterminer « le plus grand ». Voici l’ordre numérique par défaut:

Et voici ce qui se passe si nous utilisons l’ « ordre symbolique » à la place:

Il y a toujours beaucoup de détails à peaufiner. Par exemple, dans la version 14.2, il y a une mise à jour de MoonPhase et des fonctions associées, avec à la fois de nouvelles informations à demander et de nouvelles méthodes pour les calculer:


Dans un autre domaine, en plus de nouveaux formats majeurs d’importation/exportation (notamment pour mieux prendre en charge Tabular), il y a une mise à jour de l’importation "Markdown" qui fournit les résultats en texte brut, ainsi qu’une mise à jour de l’importation "PDF" qui produit une liste mixte de textes et d’images.
Et il y a plein d’autres choses aussi, comme vous pouvez le découvrir dans le « Résumé des nouveautés et améliorations de la version 14.2 ». D’ailleurs, il est utile de mentionner que si vous consultez une page de documentation pour une fonction particulière, vous pouvez toujours voir ce qui est nouveau dans cette version simplement en cliquant sur Afficher les modifications.