Les objets du calcul formel

## 2 Les objets du calcul formel

### 2.1 Les nombres

Les nombres peuvent être exacts ou approchés. Les nombres exacts sont les constantes prédéfinies, les entiers, les fractions d’entiers et plus généralement toute expression ne contenant que des entiers et des constantes, comme `sqrt(2)*e^(i*pi/3)`. Les nombres approchés sont notés avec la notation scientifique standard : partie entière suivie du point de séparation et partie fractionnaire (éventuellement suivie de `e` et d’un exposant). Par exemple, `2` est un entier exact, `2.0` est la version approchée du même entier; `1/2` est un rationnel, `0.5` est la version approchée du même rationnel. Xcas peut gérer des nombres entiers en précision arbitraire : essayez de taper `500!` et comptez le nombre de chiffres de la réponse.

On passe d’une valeur exacte à une valeur approchée par `evalf`, on transforme une valeur approchée en un rationnel exact par `exact` Les calculs sont effectués en mode exact si tous les nombres qui interviennent sont exacts. Ils sont effectués en mode approché si un des nombres est approché. Ainsi `1.5+1` renvoie un nombre approché alors que `3/2+1` renvoie un nombre exact.

    sqrt(2)
    evalf(sqrt(2))
    sqrt(2)-evalf(sqrt(2))
    exact(evalf(sqrt(2)))*10^9
    exact(evalf(sqrt(2)*10^9))

Pour les nombres réels approchés, la précision par défaut est proche de 14 chiffres significatifs (la précision relative est de 53 ou 45 bits pour les réels flottants normalisés selon les versions de Xcas). Elle peut être augmentée, en donnant le nombre de décimales désiré comme second argument de `evalf`.

    evalf(sqrt(2),50)
    evalf(pi,100)

On peut aussi changer la précision par défaut pour tous les calculs en modifiant la variable `Digits`.

    Digits:=50
    evalf(pi)
    evalf(exp(pi*sqrt(163)))

La lettre `i` est réservée à √−1 et ne peut être réaffectée ; en particulier on ne peut pas l’utiliser comme indice de boucle.

    (1+2*i)^2
    (1+2*i)/(1-2*i)
    e^(i*pi/3)

Xcas distingue l’infini non signé `infinity` (∞), de `+infinity` ou `inf` (+∞) et de `-infinity` ou `-inf`(−∞).

    1/0; (1/0)^2; -(1/0)^2

Constantes prédéfinies
`pi` | π≃ 3.14159265359
`e` | e≃ 2.71828182846
`i` | i=√−1
`infinity` | ∞
`+infinity` ou `inf` | +∞
`-infinity` ou `-inf` | −∞

### 2.2 Les caractères et les chaînes

Une chaîne est parenthésée par des guillemets ("). Un caractère est une chaîne ayant un seul élément.

    s:="azertyuiop"
    size(s)
    s[0]+s[3]+s[size(s)-1]
    concat(s[0],concat(s[3],s[size(s)-1]))
    head(s)
    tail(s)
    mid(s,3,2)
    l:=asc(s)
    ss:=char(l)
    string(123)
    expr(123)
    expr(0123)

Chaînes
`asc` | chaîne->liste des codes ASCII
`char` | liste des codes ASCII->chaîne
`size` | nombre de caractères
`concat` ou `+` | concaténation
`mid` | morceau de chaîne
`head` | premier caractère
`tail` | chaîne sans le 1ier caractère
`string` | nombre ou expression->chaîne
`expr` | chaîne->nombre (base 10 ou 8) ou expression

### 2.3 Les variables

On dit qu’une variable est formelle si elle ne contient aucune valeur : toutes les variables sont formelles tant qu’elles n’ont pas été affectées (à une valeur). L’affectation est notée `:=`. Au début de la session `a` est formelle, elle devient affectée après l’instruction `a:=3`, `a` sera alors remplacé par 3 dans tous les calculs qui suivent, et `a+1` renverra 4. Xcas conserve tout le contenu de votre session. Si vous voulez que la variable `a` après l’avoir affectée, soit à nouveau une variable formelle, il faut la "vider" par `purge(a)`. Dans les exemples qui suivent, les variables utilisées sont supposées avoir été purgées avant chaque suite de commandes. Il ne faut pas confondre

- le signe `:=` qui désigne l’affectation
- le signe `==` qui désigne une égalité booléenne : c’est une opération binaire qui retourne 1 ou 0 (1 pour true qui veut dire Vrai et 0 pour false qui veut dire Faux)
- le signe `=` utilisé pour définir une équation.

    a==b
    a:=b
    a==b
    solve(a=b,a)
    solve(2*a=b+1,a)

On peut faire certains types d’hypothèses sur une variable avec la commande `assume`, par exemple `assume(a>2)`. Une hypothèse est une forme spéciale d’affectation, elle efface une éventuelle valeur précédemment affectée à la variable. Lors d’un calcul, la variable n’est pas remplacée mais l’hypothèse sera utilisée dans la mesure du possible, par exemple `abs(a)` renverra `a` si on fait l’hypothèse `a>2`.

    sqrt(a^2)
    assume(a<0)
    sqrt(a^2)
    assume(n,integer)
    sin(n*pi)

La fonction `subst` permet de remplacer une variable dans une expression par un nombre ou une autre expression, sans affecter cette variable.

    subst(a^2+1,a=1)
    subst(a^2+1,a=sqrt(b-1))
    a^2+1

Remarque : pour stocker une valeur dans une variable par référence, par exemple pour modifier une valeur dans une liste (un vecteur, une matrice), sans recréer une nouvelle liste mais en modifiant en place la liste existante, on utilise l’instruction `=<` au lieu de `:=`. Cette instruction est plus rapide que l’instruction `:=`, car elle économise le temps de copie de la liste.

### 2.4 Les expressions

Une expression est une combinaison de nombres et de variables reliés entre eux par des opérations : par exemple `x^2+2*x+c`.

Lorsqu’on valide une commande, Xcas remplace les variables par leur valeur si elles en ont une, et exécute les opérations.

    (a-2)*x^2+a*x+1
    a:=2
    (a-2)*x^2+a*x+1

Certaines opérations de simplification sont exécutées automatiquement lors d’une évaluation :

- les opérations sur les entiers et sur les fractions, y compris la mise sous forme irréductible
- les simplifications triviales comme x+0=x, x−x=0, x1=x…
- quelques formes trigonométriques comme cos(−x)=cos(x), cos(π/4)=√2/2, tan(π/4)=1…

Nous verrons dans la section suivante comment obtenir plus de simplifications.

### 2.5 Développer et simplifier

En-dehors des règles de la section précédente, il n’y a pas de simplification systématique. Il y a deux raisons à cela. La première est que les simplifications non triviales sont parfois coûteuses en temps, et le choix d’en faire ou non est laissé à l’utilisateur ; la deuxième est qu’il y a en général plusieurs manières de simplifier une même expression, selon l’usage que l’on veut en faire. Les principales commandes pour transformer une expression sont les suivantes :

- `expand` : développe une expression en tenant compte uniquement de la distributivité de la multiplication sur l’addition et du développement des puissances entières.
- `normal` et `ratnormal` : d’un bon rapport temps d’exécution-simplification, elles écrivent une fraction rationnelle (rapport de deux polynômes) sous forme de fraction irréductible développée; `normal` tient compte des nombres algébriques (par exemple comme `sqrt(2)`) mais pas `ratnormal`. Les deux ne tiennent pas compte des relations entre fonctions transcendantes (par exemple comme `sin` et `cos`).
- `factor` : un peu plus lente que les précédentes, elle écrit une fraction sous forme irréductible factorisée.
- `simplify` : elle essaie de se ramener à des variables algébriquement indépendantes avant d’appliquer `normal`. Ceci est plus coûteux en temps et "aveugle" (on ne contrôle pas les réécritures intermédiaires). Les simplifications faisant intervenir des extensions algébriques (par exemple des racines carrées) nécessitent parfois deux appels et/ou des hypothèses (`assume`) pour enlever des valeurs absolues avant d’obtenir la simplification souhaitée.
- `tsimplify` essaie de se ramener à des variables algébriquement indépendantes mais sans appliquer `normal` ensuite.

Dans le menu `Expression` du bandeau supérieur, les sous-menus sont des menus de réécriture et contiennent d’autres fonctions, pour des transformations plus ou moins spécialisées.

    b:=sqrt(1-a^2)/sqrt(1-a)
    ratnormal(b)
    normal(b)
    tsimplify(b)
    simplify(b)
    simplify(simplify(b))
    assume(a<1)
    simplify(b)
    simplify(simplify(b))

La fonction `convert` permet de passer d’une expression à une autre équivalente, sous un format qui est spécifié par le deuxième argument.

    convert(exp(i*x),sincos)
    convert(1/(x^4-1),partfrac)
    convert(series(sin(x),x=0,6),polynom)

Transformations
`simplify` | simplifier
`tsimplify` | simplifier (moins puissant)
`normal` | forme normale
`ratnormal` | forme normale (moins puissant)
`expand` | développer
`factor` | factoriser
`assume` | rajout d’hypothèses
`convert` | transformer en un format spécifié

### 2.6 Les fonctions

De nombreuses fonctions sont déjà définies dans Xcas, en particulier les fonctions classiques. Les plus courantes figurent dans le tableau ci-après; pour les autres, voir le menu `Cmds->Reel->Special`.
Sinon l’utilisateur peut définir ses propre fonctions, par exemple :

- Définition d’une fonction d’une variable :
  f(x):=x*exp(x) ou
  f:=x->x*exp(x) ou
  f:=unapply(x*exp(x),x)
- Définition de sa dérivée :
  g:=function_diff(f) ou
  g:=unapply(diff(f(x),x),x))
  ATTENTION g(x):=diff(f(x),x) N’EST PAS VALABLE ! car ce qui est à droite de := n’est pas évalué lors de la définition....il faut utiliser unapply.
- Définition d’une fonction de 2 variables :
  h(r,t):=(r*exp(t),r*t) ou
  h:=(r,t)->(r*exp(t),r*t);
- Définition à partir d’une fonction de 2 variables, d’une fonction qui à 1 variable fait correspondre une fonction:
  k(t):=unapply(h(r,t),r)
  ATTENTION Ici k(t) est une fonction de la variable r qui à r fait correspondre h(r,t). On a par exemple : k(1)(2)=(2*exp(1),2). Là aussi, il faut utiliser unapply.

Fonctions classiques
`abs` | valeur absolue
`sign` | signe (-1,0,+1)
`max` | maximum
`min` | minimum
`round` | arrondi
`floor` | partie entière (plus grand entier ≤)
`frac` | partie fractionnaire
`ceil` | plus petit entier ≥
`re` | partie réelle
`im` | partie imaginaire
`abs` | module
`arg` | argument
`conj` | conjugué
`affixe` | affixe
`coordonees` | coordonnées
`factorial ou !` | factorielle
`sqrt` | racine carrée
`exp` | exponentielle
`log` | logarithme naturel
`ln` | logarithme naturel
`log10` | logarithme en base 10
`sin` | sinus
`cos` | cosinus
`tan` | tangente
`cot` | cotangente
`asin` | arc sinus
`acos` | arc cosinus
`atan` | arc tangente
`sinh` | sinus hyperbolique
`cosh` | cosinus hyperbolique
`tanh` | tangente hyperbolique
`asinh` | argument sinus hyperbolique
`acosh` | argument cosinus hyperbolique
`atanh` | argument tangente hyperbolique

Pour créer une nouvelle fonction, il faut la déclarer à l’aide d’une expression contenant la variable. Par exemple l’expression x2−1 est définie par `x^2-1`. Pour la transformer en la fonction f qui à x associe x2−1, trois possibilités existent :

    f(x):= x^2-1
    f:=x->x^2-1
    f:=unapply(x^2-1,x)

    f(2);
    f(a^2);

Si `f` est une fonction d’une variable et `E` est une expression, `f(E)` est une autre expression. Il est essentiel de ne pas confondre fonction et expression. Si on définit : `E:=x^2-1`, alors la variable `E` contient l’expression x2−1. Pour avoir la valeur de cette expression en x=2 il faut écrire `subst(E,x=2)` et non `E(2)` car `E` n’est pas une fonction. Lorsqu’on définit une fonction, le membre de droite de l’affectation n’est pas évalué. Ainsi l’écriture `E:=x^2-1; f(x):=E` définit la fonction f: x ↦ E car `E` n’est pas évalué. Par contre `E:= x^2-1; f:=unapply(E,x)` définit bien la fonction f: x↦ x2−1 car `E` est évalué.

On peut ajouter et multiplier des fonctions, par exemple `f:=sin*exp`. Pour composer des fonctions, on utilise l’opérateur `@` et pour composer plusieurs fois une fonction avec elle-même, on utilise l’opérateur `@@`.

    f:=x->x^2-1;
    (f@f)(2);
    (f@sqrt)(a);
    f1:=f@sin
    f2:=f@f
    f3:=f@@3
    f1(a)
    f2(a)
    f3(a)

On peut définir des fonctions de plusieurs variables à valeurs dans ℝ comme :
`f(x,y):=x+2*y`
et des fonctions de plusieurs variables à valeurs dans ℝp par exemple :
`f(x,y):=(x+2*y,x-y)`

### 2.7 Listes, séquences, ensembles

Xcas distingue plusieurs sortes de collections d’objets, séparés par des virgules :

- les listes (entre crochets)
- les séquences (entre parenthèses)
- les ensembles (entre pourcentage-accolades)

    liste:=[1,2,4,2]
    sequence:=(1,2,4,2)
    ensemble:=%{1,2,4,2%}

Les listes peuvent contenir des listes (c’est le cas des matrices), alors que les séquences sont plates (un élément d’une séquence ne peut pas être une séquence). Dans un ensemble, l’ordre n’a pas d’importance et chaque objet est unique. Il existe une autre structure, appelée table, dont nous reparlerons plus loin.

Il suffit de mettre une séquence entre crochets pour en faire une liste ou entre accolades précédées de `%` pour en faire un ensemble. On passe d’une liste à sa séquence associée par `op`, d’une séquence à sa liste associée en la mettant entre crochets ou avec la fonction `nop`. Le nombre d’éléments d’une liste est donné par `size` (ou `nops`).

    se:=(1,2,4,2)
    li:=[se]
    op(li)
    nop(se)
    nops(se)
    %{se%}
    size([se])
    size(%{se%})

Pour fabriquer une liste ou une séquence, on utilise des commandes d’itération comme `$` ou `seq` (qui itèrent une expression) ou `makelist` (qui définit une liste à l’aide d’une fonction).

    1$5
    k^2 $ (k=-2..2)
    seq(k^2,k=-2..2)
    seq(k^2,k,-2..2)
    [k^2$(k=-2..2)]
    seq(k^2,k,-2,2)
    seq(k^2,k,-2,2,2)
    makelist(x->x^2,-2,2)
    seq(k^2,k,-2,2,2)
    makelist(x->x^2,-2,2,2)

La séquence vide est notée `NULL`, la liste vide `[]`. Pour ajouter un élément à une séquence il suffit d’écrire la séquence et l’élément séparés par une virgule. Pour ajouter un élément à une liste on utilise `append`. On accède à un élément d’une liste ou d’une séquence grâce à son indice mis entre crochets, le premier élément étant d’indice 0.

    se:=NULL; se:=se,k^2$(k=-2..2); se:=se,1
    li:=[1,2]; (li:=append(li,k^2))$(k=-2..2)
    li[0],li[1],li[2]

Les polynômes sont souvent définis par une expression, mais ils peuvent aussi être représentés par la liste de leurs coefficients par ordre de degré décroissant, avec comme délimiteurs `poly1[` et `]`. Il existe aussi une représentation pour les polynômes à plusieurs variables. Les fonctions `symb2poly` et `poly2symb` permettent de passer de la représentation expression à la représentation par liste et inversement, le deuxième argument détermine s’il s’agit de polynômes en une variable (on met le nom de la variable) ou de polynômes à plusieurs variables (on met la liste des variables).

Séquences et listes
`E$(k=n..m)` | créer une séquence
`seq(E,k=n..m)` | créer une séquence
`[E$(k=n..m)]` | créer une liste
`makelist(f,k,n,m,p)` | créer une liste
`op(li)` | passer de liste à séquence
`nop(se)` | passer de séquence à liste
`nops(li)` | nombre d’éléments
`size(li)` | nombre d’éléments
`sum` | somme des éléments
`product` | produit des éléments
`cumSum` | sommes cumulées des éléments
`apply(f,li)` | appliquer une fonction aux éléments d’une liste
`map(li,f)` | appliquer une fonction aux éléments d’une liste
`map(li,f,matrix)` | appliquer une fonction aux éléments d’une matrice
`poly2symb` | polynôme associé à une liste
`symb2poly` | coefficients d’un polynôme

### 2.8 Temps de calcul, place mémoire

Le principal problème du calcul formel est la complexité des calculs intermédiaires. Elle se traduit à la fois par le temps nécessaire à l’exécution des commandes et par la place mémoire requise. Les algorithmes implémentés dans les fonctions de Xcas sont performants, mais ils ne peuvent pas être optimaux dans tous les cas. La fonction `time` permet de connaître le temps d’exécution d’une commande (si ce temps est très court, Xcas exécute plusieurs fois la commande pour afficher un résultat plus précis). La mémoire utilisée apparaît dans les versions Unix dans la ligne d’état (la barre-bouton). Si le temps d’exécution d’une commande dépasse quelques secondes, il est possible que vous ayez commis une erreur de saisie. N’hésitez pas à interrompre l’exécution (bouton rouge `STOP` en haut à droite, il est conseillé de faire une sauvegarde de votre session auparavant).