Rng

30
Génération de nombres aléatoires uniformes et non uniformes en contexte distribué. Hamid BADI 10 Janvier 2008

description

Rng je sais pas quoi écrire encore désolé et à bientot, cao

Transcript of Rng

Page 1: Rng

Génération de nombres aléatoires uniformes et non uniformes en contexte 

distribué.

Hamid BADI

10 Janvier 2008

Page 2: Rng
Page 3: Rng

Table des matières ­ 1 ­  Généralités sur la génération de nombres aléatoires...................................................................4

1.1 ­  Générateurs de nombres pseudo­aléatoires ............................................................................41.2 ­  Les types de PRGN existants .................................................................................................41.3 ­  Notion de germe......................................................................................................................61.4 ­  Les générateurs quasi­aléatoires.............................................................................................6

 ­ 2 ­  Caractéristiques des PRNG.........................................................................................................72.1 ­  Uniformité...............................................................................................................................72.2 ­  Périodicité...............................................................................................................................72.3 ­  Répétabilité.............................................................................................................................82.4 ­  Efficacité................................................................................................................................82.5 ­  Indépendance..........................................................................................................................9

 ­ 3 ­  Les PRNG en contexte distribué...............................................................................................103.1 ­  MPI et contexte distribué......................................................................................................103.2 ­  Complexité de génération pseudo­aléatoire en contexte distribué........................................143.3 ­  Solutions envisageables........................................................................................................17

3.3.1   Utilisation d'un modèle maître / esclave.........................................................................173.3.2   Méthode de Nonoverlapping blocks...............................................................................173.3.3   Méthode du leapfrogging « saut de grenouille »............................................................193.3.4   Utilisation de plusieurs générateurs...............................................................................21

 ­ 4 ­  Les outils existants....................................................................................................................224.1 ­  SPRNG.................................................................................................................................22

4.1.1   Présentation.....................................................................................................................224.1.2   Utilisation.......................................................................................................................224.1.3   Exemple d'utilisation......................................................................................................264.1.4   Limites............................................................................................................................28

4.2 ­  GNU Scientific Library ­ GSL.............................................................................................294.2.1   Présentation....................................................................................................................29

 Références :.......................................................................................................................................30

Page 4: Rng

 ­ 1 ­ Généralités sur la génération de nombres aléatoiresLa génération de nombres aléatoires par des calculateurs, qui de par leur conception sont 

déterministes, fait l'objet depuis des décennies de nombreux projets de recherche en informatique. La complexité réside dans le fait que tout programme informatique détermine à partir d'une liste de valeurs en entrée, un ensemble de valeurs en sortie identique à  chaque exécution, si les valeurs d'entrées sont inchangées entre deux exécutions. Il est de ce fait impossible de générer une suite de nombres purement aléatoires avec des machines déterministes telles que les ordinateurs.

1.1 - Générateurs de nombres pseudo-aléatoires

Il est important d'introduire ici la notion de générateur de nombres pseudo­aléatoires. Ils sont aussi appelé PRNG (désignant Pseudo Random Number Generator). Le principe de ces générateurs consiste à créer à partir d'un germe initial, un nombre dit pseudo­aléatoire, qui ne présente aucun lien logique ou arithmétique apparent avec le germe. Ce nombre généré est ensuite utilisé pour créer un second nombre pseudo­aléatoire. On peut ainsi récursivement générer une suite de nombres qui n'ont, d'apparence, aucun lien logique dans leur séquence, mais qui sont en fait tous obtenus par une formule déterministe.

1.2 - Les types de PRGN existants

Le premier  d'entre  eux fut  développé  par  John Von Neumann en 1940.   Il   repose  sur   le principe de l'élévation au carré  de nombres codés sur k digits afin de générer une suite pseudo aléatoire. Une fois le nombre élevé au carré, on conserve les k digits situés entre les digits 3k/2 et k/2. Ces k digits constituent alors un nombre aléatoire et sont à nouveau utilisés pour recommencer l'opération.

1234 ² = 01522756    xx5227xx

nombre aléatoire tiré : 5227

Sur cet exemple on ne génère que des nombres de 4 digits. Il en faut en réalité beaucoup plus pour générer une suite de qualité. Nous reviendrons plus tard sur les critères de qualité d'un PRNG.

Suite  à   l'algorithme de Von Neumann de nombreuses  autres méthodes  de génération de nombres pseudo­aléatoires ont vu le jour.

Une  des  méthodes   les  plus   employées  utilise   la   congruence   linéaire.  Ces  LCG  (Linear Congruential   Generator)   utilisent   la   formule   suivante   pour   générer   une   suite   de   nombres pseudo­aléatoires.

La qualité du générateur sera en partie déterminée par le choix des paramètres a, b et m.

On peut citer les méthodes de registres à décalage bouclés (SRG, pour Shift­Register Generator), qui utilisent l'opération binaire XOR sur la séquence de bit du précédent nombre pseudo­aléatoire 

 ­ 1 ­ Généralités sur la génération de nombres aléatoires 4

Page 5: Rng

afin d’en générer un nouveau.

 ­ 1 ­ Généralités sur la génération de nombres aléatoires 5

Page 6: Rng

1.3 - Notion de germe

On appel « germe » d'un PRNG le nombre qui permet de l'initialiser. Il s'agit d'une valeur numérique donné  par  l'utilisateur sur  laquelle est effectuée l'opération arithmétique définie dans l'implémentation  du  générateur.  Ce  résultat   sera  un  nombre  pseudo­aléatoire.  Une  seule  valeur d'entrée est nécessaire à   l'initialisation car les nombres pseudo­aléatoires suivant seront calculés récursivement à partir des précédents.

On peut voir ainsi qu'à une valeur donnée pour le germe d'un PRNG, correspond une et une seule   séquence   de   nombres   pseudo­aléatoire.   Les   conséquences   de   cette   caractéristique   seront détaillées par la suite.

1.4 - Les générateurs quasi-aléatoires

Il   s'agit   de  générateurs   également   utilisés   dans   le   cadre   de   simulation   stochastique,   en particulier   dans   le   domaine   des   finances.   La   grande   différence   avec   les   générateurs pseudo­aléatoires vient du fait que la séquence générée est entièrement déterministe et ne présente aucun   caractère   aléatoire.   On   peut   donc   se   demander   d'où   vient   se   nom   de   générateur quasi­aléatoire, en anglais Quasi Random Number Generator  (QRNG).

Ce nom vient du fait que la séquence peut générer un QRNG correspond à un échantillon d'une   distribution   de   loi   de   probabilité,   alors   qu'un   PRNG   simule   une   distribution   de   loi   de probabilité [GENTLE 03]. Le but d'un QRNG est d'effectuer des tirages qui remplissent le plus uniformément possible un espace d'une dimension donnée.

Une   implémentation  de   tels   générateurs   repose   sur   l'utilisation  de   fractals.  Les   fractals effectuent  des  opérations  sur  un élément afin  de  le  diviser  en plusieurs  éléments  de structures identiques. Par exemple, prenons le segment [0­1] et effectuons l'opération de le diviser en 2. On obtient alors 2 sous ensembles [0­0,5] et ]0,5­1]. Si l'on répète l'opération sur ces 2 sous­ensembles on obtient alors 4 sous intervalles.

Ainsi de suite on peut obtenir une suite de points à scission d'un sous ensemble en 2. Cela permet d'obtenir une suite de points formant une distribution uniforme mais non aléatoire.  

 ­ 1 ­ Généralités sur la génération de nombres aléatoires 6

Page 7: Rng

 ­ 2 ­ Caractéristiques des PRNGLa qualité des générateurs pseudo­aléatoires dépend essentiellement de la faculté qu'à cette 

formule  arithmétique  à  produire  une  séquence  de nombre  vérifiant   les  principes   fondamentaux suivants :

2.1 - Uniformité

En probabilité on définit qu'une variable aléatoire X suit une loi uniforme sur un intervalle [a ­ b] si elle est absolument continue et que sa densité est définie par :

f x =1

b−asi x ∈ [a−b ]

0 sinon

La moyenne étant égale à  (b + a) / 2 et l'écart­type à (b + a)² / 12

On  dit   que   le   principe  d'uniformité   d'un  générateur   pseudo­aléatoire   est   respecté   si   les valeurs   tirées  par  ce  générateur   suivent   rigoureusement  cette   loi  uniforme.  En  d'autres   termes, chaque nombre généré par le PRNG doit avoir la même probabilité d'être tiré et ce en tout point de l'intervalle de tirage.

Cette propriété est essentielle car elle constitue la base de tout tirage aléatoire. Toutes les autres lois de probabilité classiques telle que la loi normale ou la loi exponentielle dérivent de la loi uniforme. Ainsi si le générateur est biaisé, tout algorithme qui simulerait une loi de probabilité  plus complexe serait également biaisé et fournirait par conséquent des résultats peu fiables.

2.2 - Périodicité

On définit   par  périodicité   le  nombre  de   tirages  pseudo­aléatoires  que  peut   effectuer  un PNRG avant  de retirer  un résultat  déjà   sorti.  Mathématiquement,   la  probabilité  qu'une variable aléatoire   réelle  soit  égale à  une constante est  nulle.  Il  est  donc en théorie   impossible  qu'un tel événement se produise et la période serait donc infinie.  Cependant, il est important de rappeler que même si de nombreux générateurs sont utilisés pour la génération de nombres réels, ils ne peuvent effectuer que des tirages discrets et en nombre fini d'éléments du fait du codage binaire des nombres réels. De ce fait la période ne peut excéder le nombre de réels codables par le générateur.

La période est importante car elle indique le nombre d'éléments différents que peux sortir le générateur.   Une   période   trop   courte   se   révélera   problématique   dans   des   applications   où   de nombreux tirages sont nécessaires, telle que des simulations de Monte­Carlo. En effet, rappelons qu'un PRNG utilise les précédents nombres tirés pour générer le suivant. De ce fait si un nombre a déjà été tiré par un PRNG, tous les suivants auront également été tirés. Il est donc totalement inutile de tirer plus de nombres que la période du générateur. Sinon cela conduirait à effectuer plusieurs fois exactement les mêmes tests, ce qui engendrerait un allongement du temps d'exécution sans pour autant améliorer la qualité des résultats.

 ­ 2 ­ Caractéristiques des PRNG 7

Page 8: Rng

2.3 - Répétabilité

On   appel   répétabilité   la   faculté   que   possède   un   générateur   à   fournir   facilement   deux séquences pseudo­aléatoires strictement identiques. 

On pourrait penser qu'une telle propriété soit néfaste pour des applications informatiques. En effet, si le but de la génération de nombres aléatoires est d'obtenir des nombres imprédictibles, le fait de générer deux séquences identiques de suite semble en contradiction avec ce principe.

En réalité, cela s'avère très utile dans certaines applications. D'ailleurs c'est en partie pour cet avantage que les PRNG sont préférés aux générateurs de nombres purement aléatoires pour des simulations de Monte­Carlo. En effet,   le débogage d'algorithmes qui utilise cette technique s'en trouve facilité si les résultats restent constants d'une exécution à l'autre. Cela permet d'évaluer et d'améliorer les performances de l'algorithme sans se demander si les variations dans les résultats proviennent   des   modifications   apportées,   ou   si   elles   ne   sont   que   le   résultat   d'une   séquence stochastique plus ou moins favorable.

On peut considérer que la répétabilité est respectée si, à partir d'un même germe, on peut obtenir   deux   séquences   de   nombres   pseudo­aléatoires   rigoureusement   identiques   entre   deux exécutions utilisant le même générateur. On note ici l'importance du germe qui permet à lui seul de garder la trace de la séquence aléatoire complète.  

2.4 - Efficacité

Les générateurs pseudo­aléatoires étant souvent utilisés pour des simulations stochastiques nécessitant  parfois  plusieurs  centaines  de milliers  de   tirages,   il   semble  évident  que  la   fonction chargée d'effectuer un tirage soit le plus efficace possible. Pour être efficace, le temps entre l'appel à la fonction chargée de renvoyer un nombre aléatoire et la réception de la valeur par le programme doit être extrêmement court.

C'est aussi pour cette efficacité plus importante que les PRNG sont préférés au générateurs purement aléatoires.

 ­ 2 ­ Caractéristiques des PRNG 8

Page 9: Rng

2.5 - Indépendance

On   dit   qu'un   PRNG   respete   le   critère   d'indépendance   si   il   génère   des   séquences pseudo­aléatoires   non   corrélées.  Le  niveau   de   corrélation   entre   deux   séquences   est   difficile  à déterminer.   Il   faut  pour  cela  effectuer  de  nombreux  tests   sur   les  générateurs   tels  que  DieHard [MARSAGLIA 96]  ou  le  Test  U01  [L'ECUYER 07].  On peut  voir   sur   les   images  suivante  un exemple de générateurs corrélés et non­corrélés.

Ce test  empirique de L'Ecuyer consiste a effectuer un tirage de 1000 paires de nombres pseudo­aléatoires. Chaque paire donnant les coordonées d'un point à placer dans un repère de 0 à 0,001 en absice et 0 et 1 en ordonnée.

Cela permet de distinguer ou non manière empirique des structures  redondantes qui sont les signes de générateurs corrélés. Ainsi, on peut voir une net corrélation sur le générateur rand() à gauche, alors que le générateur MLCG laisse apparaître une disposition de points d'apparence plus aléatoire.

 ­ 2 ­ Caractéristiques des PRNG 9

Générateur standard du langage C rand() Générateur MLCG de Pierre L'Ecuyer

Page 10: Rng

 ­ 3 ­ Les PRNG en contexte distribué

3.1 - MPI et contexte distribué

On parle de contexte distribué  dès lors qu'un algorithme utilise un système informatique disposant de plusieurs processeurs ayant chacun leur propre espace mémoire. De telles machines sont utilisées afin de paralléliser les programmes et ainsi accélérer leur exécution. Au lancement du programme, les segments de codes nécessaires à l'exécution sont dupliqués pour chaque processeur actif. Ce qui donne naissance à plusieurs processus qui exécuteront les même instructions.

Par exemple, un algorithme qui nécessite d'exécuter un très grand nombre de fois les mêmes instructions mais sur des données différentes, peut voir  son temps d'exécution considérablement réduit si ces calculs sont effectués en même temps par plusieurs processeurs. Ainsi au lieu d'un seul processeur  qui   exécute   les  N  instructions  nécessaires,  on  a  M processeurs  qui   effectuent  N/M instructions. On notera toutefois que cette méthode ne divise pas le temps d'exécution par le nombre de processeurs utilisés en raison des temps de communications entre les processeurs.

En effet, en contexte distribué chaque processeur dispose de son propre espace et de ce fait il est impératif de faire parvenir à chaque processeur les données nécessaires au calcul. De plus, une fois les calculs effectués il faut rassembler les résultats vers un seul processeur.

La communication entre processeur peut s'effectuer grâce à la  Message  Passing  Interface (MPI). MPI est une librairie développée sous plusieurs langages, notamment C, Fortran et C++, qui met   à   la   disposition   des   développeurs   un   certains   nombres   de   fonctions   permettant   la communication en contexte distribué. Le principe de base de fonctionnement de MPI repose sur l'envoi de message entre les différents processus.  On distingue plusieurs catégories de fonctions, ici décrite avec une implémentation en C. 

Les fonctions de contrôle d'environnement :MPI_Init(argc,argv)

Il s'agit de la fonction d'initialisation paramétrée avec les arguments de la ligne de commande. argv contient notamment le nombre de processus créés.

MPI_Comm_rank(world,rank)MPI_Comm_size(world,size)

C'est 2 fonctions permettent respectivement d'obtenir le numéro du processus qui est exécuté ainsi que le nombre total de processus en activité. Il est en effet important de connaître l'identifiant du processus   tournant   car   chaque   processeur   possède   la   même   séquence   d'instructions,   or   ils n'effectuent pas systématiquement les même actions. Le paramètre rank permet donc de tester les processus   les   uns   des   autres,   le   premier   processus   ayant   l'identifiant   0.   Le   paramètre   world, également présent dans plusieurs autres fonctions, référence toujours le communicateur utilisé. Il prend   souvent   la   valeur   désignant   le   communicateur   par   défaut   désigné   par   la   constancte MPI_COMM_WORLD.

 ­ 3 ­ Les PRNG en contexte distribué 10

Page 11: Rng

MPI_Wait(request,status)

Cette   fonction  permet  de  bloquer   l'exécution  d'un  processus   tant  qu'il  ne   reçoit  pas   la   requête désigné pas le paramètre request. Le paramètre status pointe sur une structure qui enregistre des informations comme par exemple des erreurs. Comme pour le paramètre world décrit précédement, status est utilisé dans plusieurs fonction MPI et possède dans chaque cas les même propriétés.MPI_Finalize()

Cette fonction permet de clôturer proprement le programme qui utilise MPI.

 ­ 3 ­ Les PRNG en contexte distribué 11

Page 12: Rng

Les fonctions de communication point à point :MPI_Send(buffer, size, type, dest, tag, world)MPI_Recv(buffer, size, type, src , tag, world, status)

La fonction MPI_Send permet d'envoyer un buffer de données depuis  le processeurs qui effectue l'appel vers le processeur référencé par le paramètre dest. Il est nécessaire d'indiquer dans le paramètre size le nombre d'éléments contenu dans le buffer d'envoi ainsi que le type de données envoyé grâce au paramètre  type. Enfin le paramètre  world  référence le contexte d'exécution et tag donne un identifiant au message envoyé.

La fonction MPI_Recv lors qu'elle est appelée ce mets en attente d'un buffer de données provenant du processus  src et ayant pour identifiant  tag. A la réception, les  size éléments de type type seront enregistrés dans le buffer référencé par buffer. MPI_Isend(buffer, size, type, dest, tag, world, request)MPI_Irecv(buffer, size, type, src , tag, world, request)

Les fonctions MPI_Isend() et MPI_Irecv() ont des fonctionnalité analogue aux 2 fonctions précédentes.   Elle   se   distingue   toute   fois   par   leur   caractère   non   bloquant.   Cela   signifie   que l'exécution séquentielle de l'algorithme ne s'arrête pas à l'appel de ces fonctions. Dans le cas d'un envoi, MPI_Isend() ne fait que préparer l'envoi et ne l'effectuera qu'une fois qu'il recevra la requête référencée par req. MPI_Irecv() quand à elle n'attend pas la réception totale ou partielle du buffer pour continuer l'exécution. Le développeur doit alors s'assurer de la réception du message grâce à la fonction wait() décrite précédemment.

Les fonctions de communications collectives :MPI_Bcast(buffer, size, type,root,world)

Cette fonction est similaire à MPI_Send() à la différence que l'information envoyé n'est pas destinée à un seul processus ciblé mais du processus root à l'ensemble des autres. Buffer sert à la fois de buffer d'envoi pour root et de réception pour les autres.

MPI_Scatter(send_buffer,send_size,send_type recv_buffer,recv_size,recv_type,root, world)

A l'appel de cette fonction le processus root envoi send_size élément de type send_type contenu dans le send_buffer. Chaque processus recevra ensuite, recv_size élément de type recv_type dans le buffer  recv_buffer.  Plus  simplement,   la   fonction permet  d'envoyé  un buffer contenant  beaucoup d'élément en le scindant de manière à ce que chaque processus reçoivent une partie du buffer.

MPI_Reduce(send_buffer,recv_buffer,size,type,op,root,world)

Cette   fonction   permet   de   rassembler   des   données   calculer   par   plusieurs   processeur.   Chaque processeur envoie par le buffer send_buffer,size éléments de type type. Chacun de ces éléments étant   rassemblé   par   le  processus   root  dans   le   recv_buffer   en  utilisant   l'opération  op   (addition, produit, maximum ...).

Compilation est exécution :

 ­ 3 ­ Les PRNG en contexte distribué 12

Page 13: Rng

Avant  de  pouvoir  être   exécuter,   il   est  necessaire  de  compiler   le   code   source  grâce  aux compilateurs mpicc pour un code en C et mpiCC pour un code en C++. Le programme compilé doit ensuite être exécuté grace à la commande suivante :mpirun ­np X nom_de_l'executable

L'option ­np permet d'indiquer le nombre de processus à créer, la variable x représente ce nombre de processus.

 ­ 3 ­ Les PRNG en contexte distribué 13

Page 14: Rng

3.2 - Complexité de génération pseudo-aléatoire en contexte distribué

L'intérêt   de   pouvoir   générer   des   nombres   pseudo­aléatoires   en   contexte   distribué   est directement l'intérêt même de la programmation distribué, à savoir accroître les performances d'un algorithme. Si il était impossible d'utiliser des nombres pseudo­aléatoires satisfaisant en contexte distribué,   cela   constituerait   un   frein   à   l'avancer   technologique   apporté   grâce   aux   simulations stochastiques. Le temps d'exécutions de telles simulations pouvant parfois être excessivement long sur une machine mono­processeur.

Bien qu'il soit possible de générer des nombres pseudo­aléatoires en contexte distribué, cela n'est   pas   sans   contraintes   en   raison   du   principe   de   fonctionnement   d'un   PRNG.   On   a   vu précédemment qu'un générateur aléatoire devait être initialisé avec un germe afin qu'il génère une suite  pseudo­aléatoire.  Cette   initialisation  constitue   en  contexte  distribuée  un  problème majeur. Étudions les conséquences que cela entraîne dans un exemple.

Le   programme   C   suivant   utilise   la   bibliothèque   MPI   afin   de   générer   3   nombres pseudo­aléatoires pour chaque processeurs distincts, ici 4 processeur sont utilisés. Le générateur utilisé est rand(), le générateur par défaut sous UNIX. Chaque processus effectue dans un premier temps l'initialisation de l'environnement MPI et initialise le générateur avec la constante SEED. Il affiche ensuite 3 entiers tirés avec le générateur rand().

1 > #include <stdlib.h>2 > #include <stdio.h>3 > #include <mpi.h>4 >5 > #define SEED 1483793 /* germe initiale */6 >7 > int main (int argc, char ** argv)8 > {9 > int rank,10> size;11>12> /* initialisation du contexte MPI*/13> MPI_Init(&argc,&argv);14> MPI_Comm_rank( MPI_COMM_WORLD, &rank );15> MPI_Comm_size( MPI_COMM_WORLD, &size );16>17> /*Initialisation du generateur unix standard rand */18> srand( SEED );19> 20> /*affichage de trois nombres pseudo-aléatoires par processus*/21> printf( "\n------- Processus %d -------\n", rank );22> printf( "nombre 1 %d :\n",rand() );23> printf( "nombre 2 %d :\n",rand() );24> printf( "nombre 3 %d :\n",rand() );25>26> MPI_Finalize();27> return 0;

 ­ 3 ­ Les PRNG en contexte distribué 14

Page 15: Rng

28> }

 ­ 3 ­ Les PRNG en contexte distribué 15

Page 16: Rng

On obtient en sortie de l'exécution les résultats suivant :------- Processus 0 -------

nombre 1 : 1682786746

nombre 2 : 727574205

nombre 3 : 1824008005

------- Processus 3 -------

nombre 1 : 1682786746

nombre 2 : 727574205

nombre 3 : 1824008005

------- Processus 1 -------

nombre 1 : 1682786746

nombre 2 : 727574205

nombre 3 : 1824008005

------- Processus 2 -------

nombre 1 : 1682786746

nombre 2 : 727574205nombre 3 : 1824008005

On distingue   immédiatement   que   les   4   séquences   sont   identiques.  Ces   résultats   étaient prévisibles. En effet, on peut voir ligne 18 que l'on initialise le générateur rand() avec la constante SEED   définie   ligne   5.   Cette   initialisation   est   faite   dans   chacun   des   processus,   donc   chaque processus va créer une séquence pseudo­aléatoire à partir du même germe et par conséquent générer la même séquence.

Une solution trivial serait d'avoir un germe différent par processus. Cette méthode générerait bien  une  séquence  différente  par  processeur.  La  question  qui   se  pose  alors  est   :  quels  germes utiliser ? 

Comme pour l'exemple, il est possible de définir autant de constantes différentes qu'il y a de processus   créés.  Cependant,   un   choix   arbitraire   des   germes  peut   conduire   à   des   résultats   très insatisfaisant   [GENTLE   03].   Il   est   en   effet   impossible   de   savoir   dans   quelles   mesures   seront corrélées les  séquences. Certes, il se pourra dans certains cas qu'elles soient très indépendantes ou au contraire extrêmement corrélées. Il se peut même que les séquences se chevauchent dans le cas où un processus génère un nombre déjà tiré par un autre processus. On se retrouverait alors avec 2 séquences identiques ce qui nuirait à la qualité des résultats.

Bien que l'une des principales applications des PRNG soient de simuler des phénomènes nstochastiques, il est nécessaire que les développeurs du modèle de simulation soient certains de la fiabilité du simulateur. En effet, les aléas des résultats fournis à l'issu de la simulation doivent être conditionnés exclusivement par les lois de probabilités définies par les paramètres du modèles. La qualité de la séquence générée à chaque répétition de la simulation doit être garantie. Dans le cas contraire il devient difficile de statuer entre : les résultats sont­ils le fruit d'un scénario probable ou ont­ils   étaient   engendrés   par   un   comportement   "exceptionnel"   du   PRNG.   La   réalisation   d'une simulation de qualité nécessite donc un générateur fiable à chaque exécution.

 ­ 3 ­ Les PRNG en contexte distribué 16

Page 17: Rng

3.3 - Solutions envisageables

3.3.1  Utilisation d'un modèle maître / esclaveUne solution viable serait de séparer la génération de nombres pseudo­aléatoires des calculs 

qui utiliserons ces nombres. Dans ce contexte un processus qu'on peut appeler "maître", n'effectue que de la génération de nombres pseudo­aléatoire, tandis que les multiples processus "travailleurs" effectuent les calculs appropriés. Il est alors nécessaire d'utiliser les fonctions de communication intégrées  dans  MPI  afin  que   le  maître  puisse   envoyer  des  nombres   aux   travailleurs  et  que   les travailleurs   effectuent   des   requête   au   maître   lorsqu'ils   ont   besoin   d'une   nouvelle   séquence stochastique.

Avec cette technique il n'y a pas de problèmes de corrélation, car la génération de nombre pseudo­aléatoire se fait  comme dans tout programme séquentiel  standard (programme n'utilisant qu'un   seul   processeur).  L'inconvénient   vient   du   fait   que   le   programme  est   ralentit   par   le   seul processeur qui génère les nombres alors que plusieurs effectuent les calculs et attendent de recevoir de nouvelles données. Le gain en temps d'exécution entre le programme séquentiel et le parallèle s'en trouve donc réduit.

3.3.2  Méthode de Nonoverlapping blocksLa première que décrivent décrivent Cristopher A. Stoner et Paul Coddington [STONER 94] 

consiste à calculer à l'avance un germe pour chaque séquence distincte à générer. Ce mécanisme est nommé sous le terme look­ahead. Reprenons la formule arithmétique standard des générateurs congruentiels :

Si l'on fixe b à 0 on peut aboutir à la formule suivante :

x : représente la suite aléatoiren : désigne le nombre d'itération du générateurk : désigne le nombre de valeurs "passées" grâce au look­aheada,m : paramètres du générateur congruentiel

Grâce à cette propriété il est possible de connaître à l'avance par un calcul simple la valeur que générera le PRNG dans k itérations. Il est ainsi possible de calculer des germes différent pour chaque processus et avec lesquels nous seront sur qu'il n'y aura pas de chevauchement. D'où le nom de Nonoverlapping Blocks donné par James E. Gentle [GENTLE 03].

Le principe consiste à envoyer à chaque processus un germe différent mais qui font tous partie  de  la  même séquence  aléatoire  mais  à  des   indices  différents.   Il  est  possible  d'adapter   la formule  afin  d'intégrer   le  nombre  de   tirage  à   effectuer  par  processeur  ainsi  que   le  numéro  du 

 ­ 3 ­ Les PRNG en contexte distribué 17

Page 18: Rng

processus dans le calcul de chaque germe.

i : numéro du processusxi : désigne le germe du ieme processusx0  : désigne le germe initiale donné par l'utilisateurk : désigne le nombre de valeurs "passées" grâce au look­aheada,m : paramètres du générateur congruentielA   noter   que   le   calcul   de  aki+1  peut   se   révéler   coûteux   en   raison   de   la   gestion   des 

dépassements de capacités.

Au début de l'éxécution,  on calcule pour chaque processus  i,  de 0 à  p­1 (p  désignant  le nombre de processus), un germe correspondant au (n*i)eme nombre aléatoire de la séquence. Ces germes sont ensuite distribué à chaque procesus qui pourront alors générer des nombres aléatoires de façon "classique".

 ­ 3 ­ Les PRNG en contexte distribué 18

Page 19: Rng

3.3.3  Méthode du leapfrogging « saut de grenouille »Cette   méthode   utilise   comme   la   précédente   un   mécanisme   de   prévision   des   nombres 

aléatoires qui seront tirés dans k itérations. La différence se tient dans le fait, qu'au lieu de calculer un germe situé trés loin en avant de la séquence une seule fois pour chaque processus, on le calcule à chaque itérations pour chaque processus mais en recherchant beaucoup moins loin. La formule suivante   permet   de   calculer   le   prochain   nombre   aléatoire   tiré   pour   chaque   processus  i  :

x : représente la séquence aléatoiren : nombre d'itération du générateur par processus p : nombre total de processusi  : numéro de processusk : nombre d'éléments "sautés" dans la séquence

Pour être efficace  k  doit  être supérieur ou égale au nombre de processus  p.  Dans le cas contraire   les  processus dont   l'identifiant  i  serait  supérieur  ou égale à  k  généreraient   les  mêmes valeurs que les processus d'identifiant i – k. 

A chaque itération, tous les processus de 0 à p calculent le pième  nombre aléatoire suivant à partir  de sa propre position.  Chaque processus génère ainsi  des nombres pseudo­aléatoire d'une même   séquence.

L'un   des   inconvénient   des   générateurs   linéaires   qui   utilisent   ces   2   méthodes   est   qu'ils possèdent une faible période ce qui peut être préjudiciable dans des applications parallèles où le nombre   de   tirages   peut   être   extrêmement   grand.   Afin   d'augmenter   la   périodicité,   on   utilise généralement une méthode de brassage de plusieurs générateurs. 

De plus, on peut assister à une forte corrélation entre les diverses sous­suites générées par les méthodes du leapfrogging et du nonoaverlapping blocks. Une combinaison des 2 méthodes peut 

 ­ 3 ­ Les PRNG en contexte distribué 19

Page 20: Rng

contribuer à diminuer ces corrélations,

 ­ 3 ­ Les PRNG en contexte distribué 20

Page 21: Rng

3.3.4  Utilisation de plusieurs générateursUne   autre   technique   pour   générer   des   séquences   stochastiques   indépendantes   dans   un 

contexte distribué est d'utiliser un générateur diffèrent par séquence. Les générateurs étant différent les   séquences   produites   seront   donc   forcément   différentes   et   vraisemblablement   non   corrélées [GENTLE 03]. De plus il n'y a pas de possibilité de chevauchement car même si deux générateurs produisent le même nombre pseudo­aléatoire, ils ne généreront pas le même chiffre à l'état n+1.

Afin d'obtenir un générateur différent par processeur, il est possible d'utiliser un générateur congruentiel  mais  dont   les  paramètres   sont   spécifiques  à   chaque  processus.  Ainsi   la   constante multiplicative de ces générateurs peut être généré par un autre générateur pseudo­aléatoire.

Il  est  également possible d'utiliser  des générateurs de type complètement différents  pour chaque processeur, cependant il se pose un problème de "scalabilité" (par extension de l'anglais scalablity pour extensibilité). En effet, un PRNG fonctionnant en contexte distribué doit pouvoir le faire sur un nombre variables de processus et par conséquent il faut assurer de pouvoir affecter un générateur différent par processus.

 ­ 3 ­ Les PRNG en contexte distribué 21

Page 22: Rng

 ­ 4 ­ Les outils existants

4.1 - SPRNG

4.1.1  PrésentationDéveloppé par des équipes de l'université de l'état de Floride (The Department Of Computer  

Science  and   the  School   Of   Computational   Science  at  Florida   State   University),   SPRNG   est l'acronyme   de   Scalable   Parallel   Random   Number   Generator   [MASCAGNI   00].   Il   s'agit   d'une librairie   permettant   la   génération   de   séquences   aléatoires   distinctes   et   non   corrélées   dans   des contextes distribués pour chaque processus. Cette librairie est disponible en 2 versions. La version 2.0 écrite pour les langage Fortran et C et la version 4.0 qui est développée pour le C++ et toujours le Fortran. La dernière version date à ce jour du 7 juin 2007.

SPRNG implémente 6 générateurs différents et utilise la bibliothèque MPI afin d'effectuer les  échanges d'informations entre  les processus nécessaires au fonctionnement du générateur.  A l'initialisation du générateur un germe est calculé  pour chaque processus grâce à  la méthode du nonoverlapping blocks à partir du paramètre passé à la fonction d'initialisation. Après cette phase d'initialisation   chacun  de   ces   générateurs   peuvent  être   utilisés   aussi   simplement   que  n'importe qu'elle   autre   générateur   utilisé   dans  un   algorithme   séquentiel.   Chaque  générateur   effectue  des tirages selon une loi uniforme entre 0 et 1, et renvoie donc des réels double précision entre 0 et 1.

4.1.2  UtilisationSPRNG fournit 2 interfaces distincte référencé par l'interface simple et l'interface par défaut 

(default). L'interface simple est utilisée si il n'est pas nécessaire de distinguer plusieurs séquences stochastique   sur   un   même   processeur.   Si   on   utilise   cette   interface,   à   l'initialisation   une   seule séquence sera généré  sur chaque processeur, chaque séquence étant distincte de celle des autres processus. L'interface default permet d'utiliser plusieurs séquences distinctes sur chaque processeur. Chacune des séquences étant référencée par un identifiant.

Les descriptions suivantes utilisent la version 4.0 de SPRNG avec un code en C++.

Interface Simple:

Inclusions :

L'utilisation de SPRNG nécessite l'inclusion des fichiers d'en­tête sprng_.h. Il doit de plus définir les 2 macros SIMPLE_SPRNG et USE_MPI.#define SIMPLE_SPRNG           /* 2 macros à définir pour utiliser         */#define USE_MPI                /* l'interface simple                       */                               /* /!\ à définir avant l'inclusion qui suit */#include "sprng.h"             /* fichier d'en­tête de SPRNG               */

 ­ 4 ­ Les outils existants 22

Page 23: Rng

 ­ 4 ­ Les outils existants 23

Page 24: Rng

Initialisation :

Elle se fait par le simple appel à la fonction init_sprng() :int *init_sprng(int seed, int param, int rng_type = 0)

seed  :   représente   le   germe   initialparam  : sert à paramétrer certains générateur tel que le générateur congruentiel linéaire. Se 

paramètre   peut   prendre   la   valeur   de   la   constante   SPRGN_DEFAULT.rng_type: représente le type de générateur à utiliser parmi lesquels :

0 : Combined Multiple Recursive Generator

1 : 48 Bit Linear Congruential Generator with Prime Addend

2 : 64 Bit Linear Congruential Generator with Prime Addend

3 : Modified Lagged Fibonacci Generator

4 : Multiplicative Lagged Fibonacci Generator

5 : Prime Modulus Linear Congruential Generator

Si le paramètre n'est pas précisé, le Combined Mulitple Recursive Generator est utilisé

Utilisation :

Une fois initialisé, chaque processus peut faire appel aux fonctions sprng() et isprng() pour obtenir   respectivement   un  nombre  pseudo­aléatoire   flottant   compris   entre   0   et   1   ou  un   entier compris entre 0 et 231.double   sprng()int    isprng()

Interface par défaut :

Inclusions :

Il faut inclure le fichier des fichiers d'en­tête sprng_cpp.h.

#include "sprng_cpp.h"             /* fichier d'en­tête de SPRNG        */

Initialisation :

Dans  l'interface default,   il   faut déclarer  un pointeur  d'objet  de classe Sprng et  utilisé   la fonction SelectType() afin de choisir le type de générateur à utiliser. Cette fonction prend argument un entier désignant les même générateurs que dans l'interface simple. L'appel aux méthodes de cette objet remplace les appels de fonctions de l'interface simple.

Sprng *stream;                /*déclaration d'un pointeur sur un objet Sprng*/stream = SelectType(gtype);   /*selection du type de générateur             */stream­>init_sprng(streamNum, nbStream, seed, param)

streamNum  : désigne le numéro de stream, il a généralement la valeur du numéro de processusnbStream : nombre   total   de   séquences   à   générer

 ­ 4 ­ Les outils existants 24

Page 25: Rng

seed et param sont identiques à la version simple.

Utilisation :

L'utilisation   est   identique   à   l'interface   simple   à   la   différence   qu'il   faut   faire   appel   aux méthode d'un objet Sprng et non à une simple fonction.double   Sprng::sprng()int    Sprng::isprng()

 ­ 4 ­ Les outils existants 25

Page 26: Rng

4.1.3  Exemple d'utilisationL'exemple suivant est inspiré du site de référence de SPRNG http://sprng.cs.fsu.edu. Ce 

programme utilise l'interface default de la version 4.0 de SPRNG et la version C de la bibliothèque MPI.

#include <stdio.h>#include <mpi.h>                /* fichier d'en­tete MPI                  */#include "sprng.h"          /* fichier d'en­tete de SPRNG             */

#define SEED 985456376          /* germe unique utilise pour generer les  *//* distinctes sur chaque processus        */

int main(int argc, char *argv[]){    /*­­­­­­­­­­­­Variables et objets de manipulation de SPRNG­­­­­­­­­­­­­*/

    int streamnum,                /* identifiant de sequence stochastique   */        nstreams;                 /* nombre de sequences a generer          */

    int *stream;                /* pointeur vers un objet de type Sprng   */    /* cette objet est utilise pour tous les  */    /* appels aux methode de SPRNG            */

    int gtype = 0;                /* utilisation du Combined Multiple       */    /* Recursive Generator                    */

    /*­­­­­­­­­­­­­­­­­­Variables de manipulation de MPI­­­­­­­­­­­­­­­­­­­­*/

    int myid,                     /* identifiant du processus en cours      */        nprocs;                   /* nombre total de processus              */

    /*­­­­­­­­­­­­­­­­­­­­­­­­­­autres variables­­­­­­­­­­­­­­­­­­­­­­­­­­­­*/

    int i;                        /* indice de boucle                       */    double rn;                    /* enregistre un nombre pseudo­aleatoire  */

    /*­­­­­­­­­­­­­­­­­­­­­­­Configuration d'MPI­­­­­­­­­­­­­­­­­­­­­­­­­­­­*/

    MPI_Init(&argc, &argv);                  /* initialisation de MPI       */    MPI_Comm_rank(MPI_COMM_WORLD, &myid);    /* enregistrement du numero de */     /* processus                   */    MPI_Comm_size(MPI_COMM_WORLD, &nprocs);  /* enregistrement du nombre de */     /* processus                   */

    /*­­­­­­­­­­­­­­­­­­­­­­­­­­­Initialisation­­­­­­­­­­­­­­­­­­­­­­­­­­­­­*/

    streamnum = myid;     /*l'identifiant de sequence est egale au numero de*/    nstreams = nprocs;    /*et on veut autant de sequences que de processus */

    /* instanciation d'un generateur SPRNG de type gtype                    */

    /* initialisation de la sequence stochastique avec les parametres de    */

 ­ 4 ­ Les outils existants 26

Page 27: Rng

    /* generateur standards (SPRGN_DEFAULT)                                 */    stream = init_sprng(0,streamnum,nstreams,SEED,SPRNG_DEFAULT);

    /*­­­­­­­­­­­­­­­­­­­­­­Affichage des resultats­­­­­­­­­­­­­­­­­­­­­­­­­*/

    /* affichage des proprietes de la sequence                              */    printf("\n\nProcessus %d, informations sur la sequence :\n", myid,print_sprng(stream));

    /* generation et affichage de trois nombres pseudo­aleatoires           */    for (i=0;i<3;i++)    {        rn = sprng(stream);               printf("Processus %d, nombre pseudo­aleatoire %d: %.14f\n", myid, i+1, rn);    }

    /*­­­­­­­­­­­­­­­­­­­­­­­Sortie de programme­­­­­­­­­­­­­­­­­­­­­­­­­­­­*/

    free_sprng(stream);         /* liberation de l'objet Sprng            */    MPI_Finalize();             /* cloture d'MPI                          */

    return 0;}

Aprés   compilation,   la   commande   suivante   permet   d'executer   le   programme  exemple   en parallèle sur 4 processeurs differents :mpirun ­np 4 exemple

Additive Lagged Fibonacci Generator

  seed = 985456376, stream_number = 2 parameter = 0

Processus 2, informations sur la sequence :

Processus 2, nombre pseudo­aleatoire 1: 0.81418942802375

Processus 2, nombre pseudo­aleatoire 2: 0.11452920359573

Processus 2, nombre pseudo­aleatoire 3: 0.85094477780857

Additive Lagged Fibonacci Generator

  seed = 985456376, stream_number = 3 parameter = 0

 ­ 4 ­ Les outils existants 27

Page 28: Rng

Processus 3, informations sur la sequence :

Processus 3, nombre pseudo­aleatoire 1: 0.67243778038410

Processus 3, nombre pseudo­aleatoire 2: 0.54868241275882

Processus 3, nombre pseudo­aleatoire 3: 0.54435642037535

Additive Lagged Fibonacci Generator

  seed = 985456376, stream_number = 1 parameter = 0

Processus 1, informations sur la sequence :

Processus 1, nombre pseudo­aleatoire 1: 0.44147640787630

Processus 1, nombre pseudo­aleatoire 2: 0.43404284173811

Processus 1, nombre pseudo­aleatoire 3: 0.84724763334503

Additive Lagged Fibonacci Generator

  seed = 985456376, stream_number = 0 parameter = 0

Processus 0, informations sur la sequence :

Processus 0, nombre pseudo­aleatoire 1: 0.50427166200642

Processus 0, nombre pseudo­aleatoire 2: 0.55843670419893

Processus 0, nombre pseudo­aleatoire 3: 0.00084777812323

Nous pouvons remarquer que l'on obtient 4 séquences differentes alors qu'un seul germe a été utilisé.

4.1.4  LimitesSPRNG est une librairie efficace, capable de générer en contexte distribué, des séquences de 

nombres pseudo­aléatoires de qualités et utilisables pour des applications scientiques. Cependant, elle est limité par le fait qu'elle ne génère des nombres pseudo­aléatoires suivant exclusivement la 

 ­ 4 ­ Les outils existants 28

Page 29: Rng

loi uniforme entre 0 et 1. Or la plupart des applications utilisant SPRNG font appel à d'autre lois de probabilité telle que la loi normale ou exponentielle. Le développeur est alors contraint de réécrire les fonctions permettant de simuler ces lois en faisant appel à des transformations qui utilise les nombres pseudo­aléatoires suivant la loi uniforme entre 0 et 1 fournient par SPRNG.

4.2 - GNU Scientific Library - GSL

4.2.1  PrésentationComme son nom l'indique, GSL [GALASSI  06] est une libraire participant au projet GNU 

fournissant des outils destinées aux applications scientifiques. Elle est distribuée sous la licence GNU – GPL (GNU – General Public Licence) la version actuelle est la 1.10 publiée le 15 septembre 2007.

La  librairie   est  destinée  aux  developpeurs  C et  C++ et  propose  prés  de  1000  fonctions utilisable dans des domaines tels que l'algèbre linéaire, le calcul vectoriel et matriciel,   le calcul differentiel, en statistiques   ect ... Une des fonctionalité les plus utiles dans notre cas concerne les fonctions simulant des distributions aléatoires et la génération de nombres pseudo­aléatoire.

En effet, GSL fournit l'implementation de fonctions pouvant simuler la plupart des lois de probabilités standards. Elle utilise des algorithmes qui utilisent la loi uniforme [0­1] afin de générer un nombre pseudo­aléatoire suivant une loi spécifique. Les nombres pseudo­aléatoires suivant cette loi uniforme sont générés par l'un des PRNG fournit par GSL. Lesquels n'étant pas prévus pour être utilisés en contexte distribué.

 ­ 4 ­ Les outils existants 29

Page 30: Rng

Références :[GENTLE 03] : 

Ramdom number generation and Monte­Carlo methods / James E. Gentle ­ 2nd édition 2003Springer – Statistic and  Computing – ISBN : 0­287­00178­6

[STONER 94]:

Random Number Generation on Parallel Computer System, proposal for 1994 NPAC REU Project Christophe A. Stoner, Paul Coddington

[MARSAGLIA 96] :

DIEHARD : A Battery of Tests of Randomness, Marsaglia G. 1996http ://stat.fsu.edu/pub/~diehard 

[L'ECUYER 07] :

TestU01:   A   C   Library   for   Empirical   Testing   of   Random   Number   GeneratorsP. L'Ecuyer et R. Simard, ACM Transactions on Mathematical Software, 33, 4, Article 22, August 2007. 

[GALASSI  06] :

GNU   Scientific   Library   Reference   Manual   ­   Revised   Second   Edition,M. Galassi et al, ISBN 0954161734 

[MASCAGNI 00]

M. Mascagni and A. Srinivasan (2000), "Algorithm 806: SPRNG: A Scalable Library for Pseudorandom Number Generation," ACM Transactions on Mathematical Software, 26: 436­461 

Références : 30