Un primo sguardo a  Mathematica

Giulio C. Barozzi
barozzi@ciram.ing.unibo.it
CIRAM - Università di Bologna

[Revisione:  Luglio 2001]

Che cos'è Mathematica?

- Mathematica  è un sistema per eseguire interattivamente calcoli numerici ed algebrici
- Mathematica  è un sistema per manipolare liste
- Mathematica  è un sistema dotato di un potente linguaggio interpretato
- Mathematica  è un sistema per produrre grafici in linguaggio PostScript
(cioè viene generato un programma PostScript che a sua volta genera una figura). La figura può essere trasformata in formato PICT, cioè in formato bitmapped, se deve soltanto essere proiettato sullo schermo; in caso contrario il formato PostScript è ottimale per la stampa.
L'immagine PostScript può essere modificata da applicazioni grafiche come Adobe Illustrator.

Com'è implementato?

Il nucleo (kernel) di Mathematica  è implementato internamente in linguaggio C; il pacchetto è distribuito in forma compilata, senza accesso al sorgente C. L'utente dialoga con il kernel attraverso un interfaccia (front-end) che è diverssa per ogni diverso computer (e.g. MS-DOS, MacOS, Unix, Windows, ecc.). 
Il kernel può essere caricato su un calcoltore remoto e i diversi utenti possono dialogare con esso attraverso le interfacce montate su terminali diversi.
Mathematica  viene distribuita con una collezione di  packages (cioè di programmi scritti nel linguaggio stesso di Mathematica) rivolti a diverse aree di interesse matematico.
Alcuni esempi:
- Algebra
- Calculus
- Linear Algebra
- Number Theory
- Statistics
giusto per citarne alcuni. 
Il numero dei packages è in costante aumento dato che utenti in tutto il mondo ne costruiscono in continuazione.  The Mathematica Journal (trimestrale, editore Wolfram Media Inc.) è un veicolo di distribuzione.

Quali sono le convenzioni usate da Mathematica?

Mathematica,  come il linguaggio C, è "case sensitive", cioè distingue tra maiuscole e minuscole. Tutte le funzioni e le costanti del sistema iniziano con una maiuscola (torneremo su questo punto in seguito).

Esempio

a = 3

A = 5

a + A

Abbiamo appena mostrato una semplice sessione interattiva con Mathematica ; l'input è in grassetto, le risposta in  chiaro.
Ogni interazione col kernel viene numerata progressivamente:
 (In[1], Out[1], In[2], Out[2], etc.).

Mathematica  usa quattro tipi di parentesi:
(   )  parentesi tonde per raggruppare
[   ]  parentesi quadre per gli argomenti delle funzioni
{   }  parentesi graffe per le liste
[[    ]] parentesi quadre doppie per gli indici di vettori e matrici.

Esempi:

Expand[(x-1)(x+1)]

Factor[%]

Il simbolo  %  (percento) indica l'ultima espressione valutata; il simbolo %% indica la penultima.
Un vettore ( = una lista) di quattro elementi:

vec = {1, 4, -3, 5}

... il secondo elemento

vec[[2]]

Una matrice è una lista di liste (le sue righe):

mat = {{1,2,3}, {3,4,-5}}

MatrixForm[%]

... il secondo elemento della seconda riga:

mat[[2,2]]

Ogni nome (simbolo, funzione) del sistema inizia con una maiuscola:

Log[2]

Per valutare numericamente il risultato occorre la funzione N :

N[%]

Tre costanti importanti :
E   --    il numero di Eulero
Pi  --    il rapporto circonferenza/diametro
I    --    l'unità immaginaria.

N[E]

Lo stesso numero con più cifre decimali:

N[E,30]

N[Pi,100]

I^2

La famosa relazione di Eulero:

E^(I Pi) + 1 == 0

Notare la differenza tra l'operatore di assegnazione  = , e la uguaglianza logica  = = .

Utilizzando la palette BasicInputs si possono utilizzare per le costanti appena ricordate i simboli tradizionali:

N[{π,  E}]

Si possono anche scrivere esponenti e deponenti nel modo tradizionale (in questa introduzione non viene spiegato  come; vedi il manuale Getting started with Mathematica, per la piattaforma usata):

(E^(I π)) +  1  ==  
      0

Aritmetica razionale e aritmetica reale

Mathematica  può eseguire calcoli in aritmetica razionale esatta:

1/2+1/7

Un esempio più interessante (ricordiamo la costante di Eulero e):

(1 + 1/10)^10

Ecco il termine di indice 1003 (reminiscenza mozartiana subliminale ... )

(1+1/1003)^1003//Short

.... un'orrenda frazione il cui valore è

N[%,20]

Possiamo mescolare numeri reali (cioè numeri col punto decimale) con numeri interi; in tal caso il calcolo viene eseguito in virgola mobile:

(1. + 1/1003)^1003

I numeri di Fermat sono definiti da

Fer[n_] := 2^(2^n) + 1

Table[Fer[n], {n, 0, 5}]

Fermat congetturò che si trattasse di numeri primi, ma ...

PrimeQ[%]

FactorInteger[Fer[5]]

Consideriamo

Fer[6]

FactorInteger[%]

Il piccolo teorema di Fermat: se p è primo e a non è un multiplo di p, allora:
 (a^(p - 1)) mod p = 1.

Table[Range[12]^12]

Mod[%, 13]

Verifichiamo che F[6] non è primo, senza fattorizzarlo:

F6 = Fer[6]

Fer[6] non è primo: infatti

PowerMod[3, F6 - 1, F6]

Torniamo sulla rappresentazione dei numeri reali. Il funzionamento della funzione N è cambiato a partire dalla Versione 4.0. Il comando N[x,k] non altera né la presentazione a schermo né la rappresentazione interna del numero, per k ≤ 16. Si osservino i seguenti esperimenti.

a  =  18/7;  b  =  1/17;

Procuriamoci due numeri in forma decimale:

a30  =  N[a, 30]; b30  =  N[b,  30];

Table[N[a30, k], {k, 2, 17}] // MatrixForm

Table[N[b30, k], {k, 2, 17}] // MatrixForm

N[a30, 10] == N[a30, 11]

Tutti i valori di N[a30, k] per k ≤ 16 sono memorizzati come "numeri di macchina".

MachineNumberQ[a30]

{MachineNumberQ[N[a30, 16]], MachineNumberQ[N[a30, 17]]}

Possiamo gestire il numero di decimali con i comandi SetAccuracy e SetPrecision. Si osseervino i seguenti esperimenti:

Table[SetAccuracy[a30, k],  {k, 3, 16}] // MatrixForm

Table[SetPrecision[a30, k],  {k, 3, 16}] // MatrixForm

Table[SetAccuracy[b30, k],  {k, 3, 16}] // MatrixForm

Table[SetPrecision[b30, k],  {k, 3, 16}] // MatrixForm

Conclusione:    Accuracy = precisione assoluta,      Precision = precisione relativa.

Un esempio relativo alla gestione delle liste:
il lancio di una moneta

Generiamo una lista casuale di 0 e 1 (cioè simuliamo il lancio di una moneta: 1 = Testa, 0 = Croce):

lista = Table[Random[Integer, {0,1}], {20}]

Verifichiamo il bilanciamento, cioè contiamo gli 1 presenti:

Apply[Plus, %]/Length[%]//N

Voglimo costruire la lista formato da quegli elementi che seguono due 1 consecutivi. Ruotiamo la lista:

lista1 = RotateLeft[lista, 1]

Andiamo a vedere in quali posizioni si sono generati dei 2 (queste posizioni corrispondono a due 1 consecutivi):

Drop[lista + lista1, -2]

Flatten[Position[%, 2]]

Spostiamoci nelle posizioni immediatamente seguenti due 1 consecutivi:

indici = %+2

Scegliamo dalla lista data gli elementi corrispondenti a tali posizioni:

lista2 = Part[lista,indici]

Controlliamo la nuova lista:

Apply[Plus, %]/Length[%]//N

Length[lista2]

Tutto daccapo con numeri "grandi", sopprimendo gli output!

prova[n_]:= 
Module[{lista, lista1, indici, freq, freq1},
	lista = 
	Table[
		Random[Integer, {0,1}], {n}];
	lista1 = RotateLeft[lista, 1];
	lista1 = Drop[lista + lista1, -2];
	indici = Flatten[
		Position[lista1, 2]]+2;
	lista1 = Part[lista,indici];
	freq = Apply[
		Plus, lista]/Length[lista]//N;
	freq1 = Apply[Plus, lista1]/Length[lista1]//N;
	Print[freq]; Print[freq1]
	]

prova[1000]

prova[10000]

Conclusione; la moneta non ha memoria; dopo due 1 consecutivi è altrettanto probabile incontrare un 1 come uno 0!

Un punto delicato: assegnazione immediata e assegnazione differita

Osserviamo la seguente sessione:

a = 3

b = a

c:= a

Nessuna risposta da parte del kernel (infatti niente è stato valutato). Abbiamo semplicemente istruito il sistema che, ogni volta viene richiesto il valore di c, esso deve valutare l'espressione a destra del segno := , cioè l'espressione semplice a. Infatti:

c

Cambiamo il valore di a:

a = 5

...e chiediamo nuovamente i valori di b e c:

b

c

Possiamo farci un'idea del funzionamento interno chiedendo informazioni su b e c :

?b

?c

Il fatto che il valore 3 sia stato attribuito a b mediante l'intermediazione di a non ha lasciato traccia.

Un utile suggerimento

Non dimenticate di ripulire le variabili di tanto in tanto. Si può fare in più modi, ma il più radicale è

Clear["Global`*"]

Come definire le funzioni

Abbimo già detto che gli argomenti delle funzioni richiedono quadre e non tonde: Sin[3] e non Sin(3).
Come si definiscono le funzioni? Ecco un esempio:

f[x_] := (x+1)^2

Il trattino di sottolineatura dopo la  x  informa il sistema che x è giusto un'etichetta che può essere sostituita non solo da un numero ma da un'espressione qualsiasi.

f[3]

f[a^2]

Expand[%]

Ecco che cosa accade se ci si dimentica il trattino di sottolineatura:

g[x] := (x+1)^2

g[3]

Il meccanismo di sostituzione non funziona!
E' saggio assegnare alle funzioni definite dall'utente nomi che iniziano con una lettera minuscola, per evitare conflitti con le funzioni di sistema. Non è una regola ferrea:

sin[x_] := x^2 + x + 1

La definizione è pericolosa, ma non illegale (il sistema ci avverte).

sin[2] + Sin[2]

N[%]

Plot[Sin[x],  {x, 0, 2  Pi}];

Plot[sin[x],  {x, -2, 2}];