Mathématica est un langage dit "fonctionnel" et mathématique, comme Maple.
1.1 Ce qu'est Mathématica 1.2 Syntaxe de Mathématica 1.3 Principes de programmation 1.4 Exemples de structure d'un package 1.5 Exemples de fonctions
2.1 Les fonctions en Mathématica 2.2 Structure d'une fonction 2.3 Paramètres et filtrage 2.4 Ecriture de "packages"
1. Présentation générale de Mathématica
1.1 Mathematica est à la fois :
- un ensemble de primitives de calculs - un outil de calcul numérique - un outil de calcul formel - un langage de programmation - un outil de visualisation graphique1.2 Syntaxe de Mathematica :
Les objets manipulés sont : - des nombres comme 1./3 et 2^92 - des valeurs formelles comme 1/3 et (x+y)^3 - des listes comme {1,17,5} Les fonctions agissent sur ces objets : - Sin[x] et Sin[Cos[{1,x}]] sont des appels de fonctions. - BesselJ[x+Iy] est une fonction élémentaire, - ParametricPlot3D aussi.1.3 Principes de programmation :
Actions élémentaires - un commentaire commence par (* et finit par *) - le symbole = réalise l'affectation - Input effectue une demande numérique - Print effectue une sortie numérique Syntaxe des Tests et Boucles - If[condition,instructionsAlors,instructionsSinon] effectue un test - Do[instructions,{compteur,[début,]fin}] est une boucle de taille fixe - While[test,instructions] est une itération conditionnelle mais de nombreuses primitives et constructions évitent ces structures. En particulier les fonctions peuvent agir sur des listes en s'exécutant pour chaque élément de la liste, les opérations classiques peuvent travailler sur des listesen mode terme à terme. De plus, un opérateur commeTable génère dynamiquement des listes. Ainsi la table de multiplication de n peut se faire soit en programmant une boucle explicite. Algorithme { n est connu, on néglige le cadrage } pour i de 1a 10 écrire i," fois ", n , " = " , i*n finpour Mathematica Do[ Print[i," fois ", n , " = " , i*n ], {i,1,n} ] (* fin de boucle *) soit en utilisant les fonctions sur listes : Table[ Print[i," fois ", n , " = " , i*n ],{i,1,n}] Ainsi, pour trouver les Nombres de Mersenne qui sont de la forme (2^p)-1 où p est premier qui sont eux-mêmes premiers, pour p entre 1 et z, on peut utiliser l'expression y=Select[x=Range[z],PrimeQ[#]&& PrimeQ[2^#-1]&] Pour z=150, il y en a 12, à savoir : {2, 3, 5, 7, 13, 17, 19, 31, 61, 89, 107, 127} Autre exemple : si on prend 10 nombres pseudo-aléatoires entre 0 et 100, pour savoir combien (et lesquels) sont supérieurs à 50, on écrit x=Table[Random[Integer,100],{10}] Length[Select[x,#<50&]] Leur position dans la liste est donnée par : y=Range[10] Select[y,x[[#]]<50&]1.4 Exemples de structure d'un package
(*** Adapted from Roman E. Maeder: Programming in Mathematica, ***) Second Edition, Addison-Wesley, 1991. BeginPackage["Skeleton`", "Package1`", "Package2`"] Needs["Package3`"] (* read in any hidden imports *) Skeleton::usage = "Skeleton.m is a package that does nothing." Function1::usage = "Function1[n] does nothing." Function2::usage = "Function2[n, (m:17)] does even more nothing." Begin["`Private`"] (* begin the private context *) protected = Unprotect[ Sin, Cos ] Aux[f_] := Do[something] staticvar = 0 Skeleton::badarg = "You twit, you called `1` with argument `2`!" Function1[n_] := n Function2[n_, m_:17] := n m /; n < 5 || Message[Skeleton::badarg, Function2, n] Sin/: Sin[x_]^2 := 1 - Cos[x]^2 Protect[ Evaluate[protected] ] (* restore protection of system symbols *) End[] Protect[ Function1, Function2 ] (* protect exported symbols *) EndPackage[] (* end the package context *) (***********************************************************) BeginPackage["OptionUse`"] g::usage = "g[n, options...] serves as an example for options." Opt1::usage = "Opt1 is an option of g[]." Opt2::usage = "Opt2 is another option of g[]." Options[g] = {Opt1 -> val1, Opt2 -> val2} Begin["`Private`"] g[ n_, opts___Rule ] := Module[ {opt1, opt2}, opt1 = Opt1 /. {opts} /. Options[g]; opt2 = Opt2 /. {opts} /. Options[g]; {n, opt1, opt2}] End[] EndPackage[] (***********************************************************) BeginPackage["Utilities`FilterOptions`"] FilterOptions::usage = "FilterOptions[symbol, options...] returns a sequence of those options that are valid." Begin["`Private`"] FilterOptions[ command_Symbol, opts___ ] := Block[{keywords = First /@ Options[command]}, Sequence @@ Select[ {opts}, MemberQ[keywords, First[#]]&]] End[] EndPackage[]1.5 Exemples de fonctions
SommeMultiIndice::usage = " SommeMultiIndice[m] génére les couples de multiIndices de somme m" SommeMultiIndice[m_List] := {} /; (Length[m] <= 0) SommeMultiIndice[m_List] := Module[ {cpl,n,k,un,deux,lng,vun,vdeu} , cpl = {} ; n = Length[m] ; k = Max @@ m ; lng = Length[ deux = un = MultiIndiceInf[n,k]] ; Do [ vun = un[[i]] ; Do[ vdeu = deux[[j]] ; If[ vun+vdeu==m, cpl = Append[cpl,List[vun,vdeu]] ] ,{j,1,lng} ] , {i,1,lng} ] ; (* fin de boucle sur i*) cpl ] (* fin de Module et donc de fonction *) ListeDeVariables[ nombre_] := Block[ {listecomplete}, listecomplete = {x,y,z,t,u,v,w,a,b,c,d,e,f,g,h} ; Take[listecomplete,nombre]] MultiIndiceInf[n_Integer?Positive,k_Integer?Positive] := MultiIndiceGen[n,k,"<="] MultiIndiceGen[n_Integer?Negative,k_Integer?Negative,kar_String] := {1} MultiIndiceGen[0,0,k:_] := {1} MultiIndiceGen[n_Integer,0,k:_] := {1} MultiIndiceGen[0,k_Integer,k:_] := {1} MultiIndiceGen[n_Integer?Positive,k_Integer?Positive,kar_String] := Block[ {ta,t0,tA,tf,long,ef1,ef2,ef3,fcd0,fcd1,fcd2,fcd3,tou}, ta = ListeDeVariablesMI[n] ; t0 = Reverse[ tA = Map[FromCharacterCode,Range[65,65+n-1]]] ; ft = Reverse[ Table[{FromCharacterCode[64+i],0,k},{i,1,n}]] ; long = StringLength[ef1= StringDrop[ToString[ft],1]] ; ef2 = StringDrop[ef1,{long,long}] ; ef3 = StringJoin[ToString[Plus @@ tA],kar,ToString[k],","] ; fcd0 = StringJoin["Table[ If[",ef3] ; fcd1 = ToString[{t0}] ; fcd2 = StringJoin[fcd0,fcd1,",{}],",ef2] ; fcd3 = StringJoin[fcd2,"]"] ; tou = Flatten[Evaluate[ ToExpression[fcd3] ],2] ; If[n>2,Flatten[tou,n-2],tou] ] (* fin de block et donc de fonction *)2. Programmation en Mathematica
2.1 Les fonctions en Mathématica
Contrairement aux langages classiques et à l'algorithmique traditionnelle qui utilise des modules (procédures et fonctions), Mathematica ne travaille que par fonction. Les fonctions sont donc tour à tour des programmes ou des sous-programmes, suivant qu'ils sont appelés directement ou par d'autres fonctions. Par exemple, si on a défini une fonction f et une fonction g, f[x] est un "programme" de paramètre x, et f[ g[x] ] est un "programme" où g est un sous-programme alors que g[ f[x] ] est un "programme" où f est un sous-programme.2.2 Structure d'une fonction
Une fonction consiste en un ensemble de définitions, regroupant : - une aide à l'utilisation de la fonction - les cas particuliers de valeurs - la déclaration et la vérification des paramètres - le détail des instructions du programme Le squelette "classique" d'une fonction (nommée pour l'exemple Fm) est : (* Commentaires pour le programmeur *) (* on conseille de mettre ici les références mathématiques et bibliographiques importantes *) (* la phrase suivante sera affichée si on tape ?Fm *) Fm :: usage = "Calcule 2^n -1 pour n entier" (* on traite le cas particulier *) Fm[0] = 1 (* et le cas général *) Fm[n_] := 2^n - 12.3 Paramètres et filtrage
Il est possible de mettre des vérifications non triviales soit dans la déclaration du paramètre, soit en fin de définition de la fonction ; ainsi, pour ne calculer Fm que sur des valeurs entières de n, on peut mettre : Fm[n_Integer?Positive] := 2^n - 1 ; pour des valeurs rationelles positives, on écrira : Fm[n_ ] := 2^n - 1 ; RationalQ[n] && n > 0. Les paramètres peuvent aussi être optionnels, pris par défaut ; ainsi, on pourrait définir : Gm[x_ ,y_ :Automatic] := Module[ baz = If[y==Automatic,1,y] ; x + baz ] (* fin de la fonction Gm *) Gm[2,3] renvoie alors 2+3 tandis que Gm[4] renvoie 4+1. Des paramètres additionnels en nombre variable (comme des options) peuvent être ajoutés avec le symbole _ _ _ ; ainsi on pourrait écrire : Gm[x_ ,y_ :Automatic,opt_ _ _ ]. Dans le corps de la fonction Gm, on pourrait alors utiliser une instruction comme : ShowVar[x,y, opt] ...2.4 Ecriture de "packages"
Il est bon de regrouper les fonctions concernant un même domaine en un "package" ; il faut alors définir un contexte (d'appel des autres fonctions). Le livre de Maeder détaille bien cela.3. Quelques exemples programmés
Une fonction qui calcule les points sur un intervalle
Interv::usage="Interv[a,b,n] Renvoie n points (10 par défaut) ; a est le premier, b le denier ]" Interv[a_,b_,n_:Automatic] := Module[ { h } , n = If[n==Automatic,10,n] ; h = (b-a)/(n-1) ; List[a + i-- h , {i,1,n} ] ] (* fin de la fonction Interv *) h est ici une variable locale, alors que i est globale (non conseillé pour i). Le résultat de la fonction est le dernier calcul effectué : il ne faut donc pas mettre de point-virgule après l'instruction List.Une fonction avec "list-ability"
CarreListe1[mal_List] := Module[ {result={}, i} , Do[ AppendTo[result,mal[[i]]^2], {i,Length[mal]} ] ; result ] (* fin de la fonction CarreListe1 *) Il est obligatoire de mettre result comme dernière instruction. On aurait pu aussi utiliser Table : CarreListe2[mal_List] := Module[ { i,n=Length[mal] } , Table[mal[[i]]^2,i,n} La fonction puissance est capable de s'appliquer elle-même à une liste, donc, on peut écrire encore plus simplement : CarreListe[bonne_List] := bonne^2 quoique la fonction Map appliquée à une évaluation de lambda calcul (nommée "fonction pure" en Mathematica) aurait aussi été une bonne solution : CarreListe3[correctSansPlus_List] := Map[#^2&,correctSansPlus] Pour rendre une fonction "listable" (= qui agit d'elle-même sur une liste), on peut prévoir à la main qu'elle agisse sur ses arguments : dif[lst_List,args___] := Map[dif[#,args]&, lst] dif[exp_,var_,rst_] := dif[ D[exp,var], rst] mais souvent le mieux est d'indiquer à Mathematica de faire ce travail à notre place ; après maf[x] := x+10, si on tape Attributes[maf] = {Listable} alors f[{51,12,37}] est interprété correctement et donne {61,22,47}.4. Mathematica : Une session commentée
Mathematica 2.0 for MS-DOS 386 Copyright 1988-91 Wolfram Research, Inc. In[1]:= << progr.m Out[1]= {Listable} Ici, Mathematica est venu lire le contenu du fichier nommé sous Dos progr.m ; la réponse correspond au dernier calcul effecté, à savoir ici, la définition de Listable comme option pour la fonction maf. Rappelons pour mémoire le contenu de ce fichier : (* Commentaires pour le programmeur *) (* la phrase suivante sera affichée si on tape ?Fm *) Fm::usage = "Calcule 2^n -1 pour n entier" Fm[0] = 1 Fm[n_] := 2^n - 1 Fmp[n_Integer?Positive] := 2^n - 1 ; Fmdr[n_ ] := 2^n - 1 /; N[n] == Rationalize[n,0] Gm[x_,y_:Automatic] := Module[ {baz = If[y===Automatic,1,y]}, x + baz Interv::usage="Interv[a,b,n] Renvoie n points (défaut 10) ; a est le premier, b le dernier ]" Interv[a_,b_,n_:Automatic] := Module[ { h,np } , np = If[n===Automatic,10,n] ; h = (b-a)/(np-1) ; Table[a + (i-1)*h , {i,1,np} ] ] (* fin de la fonction Interv *) CarreListe1[mal_List] := Module[ {result={}, i} , Do[ AppendTo[result,mal[[i]]^2], {i,Length[mal]} ] ; result ] (* fin de la fonction CarreListe1 *) CarreListe2[mal_List] := Module[ { i,n=Length[mal] } , Table[mal[[i]]^2,{i,n}]] CarreListe3[correctSansPlus_List] := Map[#^2&,correctSansPlus] dif[lst_List,args___] := Map[dif[#,args]&, lst] dif[exp_,var_,rst___] := dif[ D[exp,var], rst] maf[x_] := If[x>0,x+10,x-10] mif[x_] := x+10 muf[x_] := If[x>0,x+10,x-10] Attributes[maf] = {Listable} In[2]:= ?Fm* Fm Fmdr Fmp On demande à Mathematica quelles fonctions commencent par Fm. In[2]:= ?fm Information::notfound1: Symbol fm not found. Calcule 2^n -1 pour n entier fm n'existe pa, mais Mathematica trouve (et affiche) ce qui correspond à Fm In[2]:= ?Fm Calcule 2^n -1 pour n entier En interrogant Fm on obtient bien sûr le même résultat. In[2]:= Fm[2] Out[2]= 3 Cette valeur correspond à notre définition. In[3]:= Fm[Pi] Pi Out[3]= -1 + 2 Ici aussi : on notera l'affichage en étage (et non pas avec le symbole ^). In[4]:= N[%] Out[4]= 7.82498 Ceci donne l'équivalent numérique de l'expression précédente. In[5]:= Fm[1+I] 1 + I Out[5]= -1 + 2 Cette valeur correspond à notre définition. In[6]:= N[%] Out[6]= 0.538478 + 1.27792 I Ceci donne l'équivalent numérique de l'expression précédente. In[7]:= Fmpr[2] General::spell: Possible spelling error: new symbol name "Fmpr" is similar to existing symbols {Fmdr, Fmp}. Out[7]= Fmpr[2] Souvent, en cas de faute de frappe, Mathematica donne des orthographes correctes voisines. In[8]:= Fmp[2] Out[8]= 3 Fmp semble fonctionner comme Fm ... mais In[9]:= Fmp[-2] Out[9]= Fmp[-2] le fait de renvoyer le calcul demandé indique qu'aucune définition ne s'applique pour les négatifs In[10]:= Fmp[1+I] Out[10]= Fmp[1 + I] ni même pour les complexes In[11]:= Fmdr[1+I] 1 + I Out[11]= -1 + 2 Fmdr lui, sait calculer en complexe, In[12]:= Fmdr[Pi] Out[12]= Fmdr[Pi] toutefois Fmdr[Pi] ne fonctionne pas car ne correspond pas à la spécification. In[13]:= Gm[2] Out[13]= 3 Gm avec un argument lui rajoute 1? In[14]:= ?Gm Global`Gm Gm[x_, y_:Automatic] := Module[{baz = If[y === Automatic, 1, y]}, x + baz] La définition de Gm est unique et permet d'aditionner les deux arguments In[14]:= Gm[1,0] Out[14]= 1 comme c'est simple In[15]:= Gm[1,a] Out[15]= 1+a cela reste valable pour les calculs formels In[16]:= Interv[1,20] 28 47 22 85 104 41 142 161 Out[16]= {1, --, --, --, --, ---, --, ---, ---, 20} 9 9 3 9 9 3 9 9 De même, Interv fait la bonne découpe des points, ici en rationnel In[17]:= Interv[1,20.] Out[17]= {1, 3.11111, 5.22222, 7.33333, 9.44444, 11.5556, 13.6667, 15.7778, > 17.8889, 20.} là en réel, car on a rajouté un point à 20 In[18]:= Interv[1,10,5] 13 11 31 Out[18]= {1, --, --, --, 10} 4 2 4 avec un troisième argument, le nombre de points est variable. In[19]:= MatrixForm[%] Out[19]//MatrixForm= 1 13 -- 4 11 -- 2 31 -- 4 10 MatrixForm donne une expression plus lisible mais non cadrée. Pour cadrer, il faudrait utiliser PaddeForm. In[20]:= Interv[1,20.,5] Out[20]= {1, 5.75, 10.5, 15.25, 20.} ceci donne 5 points équidistants sur [1,20] In[21]:= MatrixForm[%] Out[21]//MatrixForm= 1 5.75 10.5 15.25 20. N s'applique à toute expression numérique. In[22]:= ml = {1,-3} Out[22]= {1, -3} ml est une liste à deux éléments : le premier est positif, le second négatif. In[23]:= CarreListe1[ml] Out[23]= {1, 9} In[24]:= CarreListe2[ml] Out[24]= {1, 9} In[25]:= CarreListe3[ml]Out[25]= {1, 9} toutes les définitions sont équivalentes en terme de résultat, pas en terme de vitesse d'exécution. In[26]:= maf[ml] Out[26]= {11, -13} In[27]:= mif[ml] Out[27]= {11, 7} In[28]:= muf[ml] Out[28]= If[{1, -3} > 0, {1, -3} + 10, {1, -3} - 10] maf, mif et muf ne sont pas équivalentes : maf et muf ont la même définition mais muf n' a pas l'option Listable ; mif est listable car sa définition utilise seulement + qui est listable. In[29]:= dif[2 x] Out[29]= dif[2 x] nos régles de différentiation ne donnent rien sur une expression In[30]:= dif[{x,x^y,x y},x,y] -1 + y -1 + y Out[30]= {dif[0], dif[x + x y Log[x]], dif[1]} mais produisent un début de résultat sur une expression suivie de varibales. In[31]:= dif[exps_] := exps In[32]:= dif[{x,x^y,x y},x,y -1 + y -1 + y Out[32]= {0, x + x y Log[x], 1} pour que dif s'exécute correctement, il faut lui donner le moyen de terminer, c'est à dire de définir quant on n'utilise plus la définition principale. Dernière minute : Mathématica effectue de nombreux calculs formels, comme l'inversion d'une matrice littérale : Factor[ Inverse[ { {a,b} , {b,c} } ] ] c b b a {{---------, --------}, {--------, ---------}} 2 2 2 2 -b + a c b - a c b - a c -b + a c ou la factorisation d'un polynôme Factor[x^4-1] 2 (-1 + x) (1 + x) (1 + x ) l'intégration de fonctions paramétrées Integrate[ (a x + b ) ^n, x ] n b a x (b + a x) (--------- + -------) a (1 + n) a + a n ou le tracé des courbes et surfaces en 3 dimensions : << packages\graphics\shapes.m Show[ Polyhedron[Icosahedron,-1,0.8], Stellate[Polyhedron[Icosahedron,0,0.9],1.1], Stellate[Stellate[Polyhedron[Icosahedron,1,1],1.1],1.1]] Programmer efficacement en Mathématica suppose bien connaitre la notion de lambda-calcul, de fonction pure, d'intégrer les concepts d'évaluation, de règles de transformations...à détailler.