Tuteur pour le langage MATHEMATICA

novalid      

Texte du Tuteur écrit par Gilles HUNAULT

Une liste des autres tuteurs (langages, logiciels, systèmes d'exploitations...) est accesible par Mosaic...


Mathématica est un langage dit "fonctionnel" et mathématique, comme Maple.

Une discussion sur cette notion, une comparaison avec Apl et Lisp est accessible en cliquant sur cette ligne.

Table des matières

1. Présentation générale de Mathematica

       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. Programmation en Mathematica

       2.1 Les fonctions en Mathématica
       2.2 Structure d'une fonction
       2.3 Paramètres et filtrage
       2.4 Ecriture de "packages"

3. Quelques exemples programmés

4. Mathematica : Une session commentée


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 graphique

1.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 - 1

2.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.