SlideShare une entreprise Scribd logo
1  sur  30
Télécharger pour lire hors ligne
Pôle Rhône-Alpes de Bioinformatique
Bâtiment Gregor Mendel
Université Claude Bernard Lyon 1
16, rue Raphaël Dubois
69622 Villeurbanne Cedex
RAPPORT DE STAGE
RECHERCHE DES MOTIFS
EN MÉMOIRE CONSTANTE DANS UN GÉNOME
NGUYEN Thi Tuong Vy
Licence 3 Informatique
Année universitaire 2015 – 2016
Maître de stage : Philippe VEBER
Ingénieur de recherche
i
Résumé
Ce rapport résume mes travaux chez Pôle Rhône-Alpes de Bio-Informatique (PRABI) pendant la
première moitié du stage et couvre mes missions prévues pendant le reste du stage.
Dans un premier temps, je vous présente l’entreprise. Le sujet du stage et les missions réalisées
seront étudiées dans la deuxième partie. À partir de là, je vais vous parler des résultats obtenus et
vous citer les tâches précédentes pour résoudre le problème. Enfin et surtout, mon retour sur
l’expérience sera montré.
L’objectif principal du stage est d’implémenter un algorithme de recherche de motifs en mémoire
constante dans un génome. Le langage principal utilisé est OCaml, un langage de programmation
fonctionnelle. Les travaux sont réalisés essentiellement sur Debian GNU/Linux.
ii
Remerciements
Je voudrais tout d’abord exprimer mes sincères remerciements et ma profonde gratitude à
Monsieur Philippe VEBER, ingénieur de recherche chez PRABI et en même temps mon maître de
stage, de son enthousiasme et patience, de son enseignement, de son suivi régulier et de ses
conseils portés sur mes travaux.
Je tiens aussi mes sincères reconnaissances à Monsieur Guy PERRIERE – Directeur du PRABI, de son
aide pour mon intégration dans la vie professionnelle et de m’avoir montré une vision concrète de
l’environnement de recherche scientifique.
iii
Sommaire
Résumé................................................................................................................................................................i
Remerciements...................................................................................................................................................ii
Introduction....................................................................................................................................................... 1
1. Environnement professionnel ................................................................................................................... 2
1.1. Organisation du PRABI....................................................................................................................... 2
1.2. Mission du PRABI-AMSB.................................................................................................................... 3
2. Mission ...................................................................................................................................................... 4
2.1. Contexte et problématique du stage ................................................................................................ 4
2.1.1. Contexte .................................................................................................................................... 4
2.1.2. Problématique........................................................................................................................... 4
2.2. Approche ........................................................................................................................................... 6
2.3. Formats des données ........................................................................................................................ 6
2.3.1. Traitement de fichier FASTA...................................................................................................... 7
2.3.2. Traitement de fichier BED ......................................................................................................... 8
2.4. Algorithmes de recherche ................................................................................................................. 8
2.4.1. Algorithme naïf (algorithme de Brute Force) ............................................................................ 9
2.4.2. Lecture de chaque caractère du fichier..................................................................................... 9
2.4.3. Construction d’une table d’états............................................................................................... 9
2.4.4. Algorithme de KMP.................................................................................................................. 10
2.4.5. Recherche d’un motif de PWM ............................................................................................... 11
2.5. Représentation des flux................................................................................................................... 12
2.5.1. Le type «générateur» .............................................................................................................. 12
2.5.2. La continuation........................................................................................................................ 13
2.6. Application aux données ................................................................................................................. 14
2.7. Travaux réalisés et leurs résultats ................................................................................................... 14
2.7.1. Benchmark d’algorithme naïf et de KMP ................................................................................ 14
2.7.2. Construction la table d’états ................................................................................................... 15
2.7.3. Lecture de fichier FASTA.......................................................................................................... 16
2.8. Travaux prévus ................................................................................................................................ 16
3. Retour sur expérience ............................................................................................................................. 17
3.1. Apprentissage du langage OCaml et de la programmation fonctionnelle...................................... 17
3.1.1. Programmation fonctionnelle ................................................................................................. 17
3.1.2. Langage OCaml........................................................................................................................ 18
3.2. Compétences techniques acquises en OCaml................................................................................. 18
3.2.1. Variable non modifiable .......................................................................................................... 18
iv
3.2.2. Typage fort............................................................................................................................... 19
3.2.3. Inférence de type..................................................................................................................... 20
3.2.4. Fonction d’ordre supérieur...................................................................................................... 20
3.2.5. Fonction récursive et fonction récursive terminale ................................................................ 21
3.2.6. Programmation impérative dans OCaml................................................................................. 21
3.2.7. Type optionnel......................................................................................................................... 22
3.2.8. Application partielle ................................................................................................................ 22
3.3. Autres compétences techniques..................................................................................................... 23
3.4. Côté professionnel........................................................................................................................... 23
Conclusion ....................................................................................................................................................... 24
Bibliographie.................................................................................................................................................... 25
1
Introduction
Mon stage se déroule au Pôle Rhône-Alpes de Bio-Informatique (PRABI), qui est l’un des six centres
régionaux membres du Réseau National des plateformes en Bioinformatique (ReNaBi). Le PRABI
regroupe plusieurs structures de la région Rhône-Alpes, en particulier le PRABI-AMSB, plateforme
Université Lyon 1, spécialisé dans l’analyse des données génomiques.
Passionnée par la biologie, intéressée par la génomique et ayant l’intention de m’orienter vers la
recherche, j’ai rejoint l’équipe du PRABI-AMSB pour découvrir les algorithmes spécifiques en
bioinformatique. Mon sujet de stage porte sur la recherche de motifs dans un génome. Mon rôle
est de modifier un algorithme connu pour qu’il réalise une recherche avec une complexité en espace
ne dépendant pas de la taille de la séquence en entrée. L’implémentation doit suivre un style
fonctionnel et être réalisée dans le langage OCaml.
Pour réaliser cette mission de 3 mois, j’ai commencé par apprendre le langage OCaml pendant 3
semaines et je travaille depuis sur une implémentation. J’ai expérimenté plusieurs algorithmes en
utilisant les différents styles de programmation en changeant la manière dont les données sont
reçues et les résultats transmis. Aussi, j’évalue les performances pour illustrer les avantages de
chaque approche. À la fin du stage, je vais faire l’intégration des fonctions produites à une
bibliothèque et la documentation correspondante.
2
1. Environnement professionnel
Dans cette partie, je vais vous présenter ma structure d’accueil, son organisation et ses
missions.
1.1. Organisation du PRABI
Le PRABI est une structure régionale qui est le groupement des structures de
bioinformatique en Rhône-Alpes. Parmi celles-ci, la composante Analyse et Modélisation
des Systèmes Biologiques (AMSB) est une plateforme de l’Université Claude Bernard
Lyon 1 (UCBL1). Dirigée par M. Guy PERRIERE, cette équipe est constituée d’ingénieurs
autour d’un besoin technique (bioinformatique).
Au sein de l’UCBL1, le PRABI-AMSB est décrit dans le figure 2 :
Figure 1. Organigramme du PRABI
Figure 2. Organigramme de UCBL1
3
Sous l’encadrement de M. Philippe VEBER – un ingénieur du LBBE – je contribue au
projet ANR ARREPRESS soutenu par l’Agence Nationale de la Recherche (ANR), et auquel
le PRABI-AMSB participe.
1.2. Mission du PRABI-AMSB
Le PRABI-AMSB est spécialisé dans l’analyse de données génomiques, notamment issues
du séquençage haut-débit, et a pour but de :
- réaliser des prestations d’analyse ou de développement logiciel en
bioinformatique ;
- proposer des formations ;
- participer aux collaborations scientifiques sur appel à projet.
Plus précisément, les applications du séquençage haut-débit comprennent :
- l’assemblage de génome ;
- la mesure de l’expression des gènes (RNA-seq) ;
- la détection des liaisons entre l’ADN et les protéines (ChIP-seq).
Figure 3. Organigramme du PRABI-AMSB
4
2. Mission
Dans cette partie, je parle d’abord de la problématique du stage et de l’approche que j’ai suivie
pour y répondre. Je vais montrer ensuite le résultat que j’ai obtenu jusqu’ici.
2.1. Contexte et problématique du stage
2.1.1. Contexte
L’équipe du PRABI et ses collaborateurs travaillent sur le projet ANR
ARREPRESS. Le but de ce projet est de déterminer les mécanismes de
répression de l’activité des gènes liés à une protéine appelée récepteur à
l’acide rétinoïque (RAR). Ainsi, ce projet concerne principalement les sites de
fixation de RAR à l’ADN. Le problème est abordé de 2 façons : détermination
expérimentale (ChIP-seq) et prédiction «in silico» sur la séquence du génome.
On souhaite comparer les résultats de la deuxième approche avec ceux de la
première, qui est plus fiable mais aussi plus coûteuse.
2.1.2. Problématique
Pour la prédiction, le modèle le plus utilisé pour les sites de liaison ADN-
protéine sur le génome est appelé matrice poids-position (en anglais :
position weight matrix - PWM). Dans le cadre du stage, je dois réaliser
l’implémentation d’un algorithme de recherche de motif en complexité
mémoire indépendante de la taille de l’entrée et de la sortie. En particulier, le
motif cherché est un PWM et la recherche se fait sur un génome complet de
souris.
D’une part, l’information du génome occupe un espace mémoire de 2,8 Go ;
d’autre part, les motifs recherchés sont courts et peu spécifiques : beaucoup
d’occurrences seront trouvées et si l’on n’a pas un mécanisme d’écriture au
fur et à mesure, cela conduira toujours à un problème d’espace mémoire.
Cela cause donc un problème de chargement mémoire du génome et
stockage des résultats.
Ce projet a deux autres contraintes. La première est d’exprimer le calcul sous
la forme des briques réutilisables. Deuxièmement, c’est d’utiliser un style
fonctionnel (c’est-à-dire limitant l’usage d’effets de bord) pour un traitement
impliquant des entrées/sorties.
Pour la première contrainte, nous avons besoin de factoriser chaque étape du
traitement : lecture depuis une source de données, analyse syntaxique,
recherche du motif, écriture de résultats. Par exemple, un programme idéal
doit pouvoir faire la recherche en utilisant la même implémentation de
l’algorithme depuis n’importe quelle source de données. Donc, il faut
5
certainement un mécanisme qui peut traiter un flux de chaines de caractères
à partir d’une source quelconque.
lecture flux
réseau
lecture
fichier
décompression parsing FASTA
recherche
motif
autres
algorithmes
unparsing
BED
écriture
fichier
compression
Figure 4. Les étapes du traitement type que l’on souhaite réaliser
FASTA et BED sont des formats très utilisés en bioinformatique.
6
2.2. Approche
Pour chaque traitement, il faut formuler une implémentation incrémentale, c’est-à-dire
capable de :
- travailler sur une entrée partielle ;
- s’interrompre quand il n’y a plus assez d’entrée ;
- reprendre lorsque les nouvelles entrées sont disponibles.
Pour rendre les algorithmes incrémentaux, on cherche à introduire un état explicite du
traitement.
En raison de la problématique et ses contraintes, mon approche se divise en 4 grandes
étapes.
Premièrement, je travaille sur les algorithmes de recherche. Pour rendre les algorithmes
incrémentaux, il me faut introduire un état explicite par l’approche de la programmation
fonctionnelle. En effet, à chaque réception de la chaine de caractères, un état sera utilisé
pour donner un résultat et un nouvel état. Cela va représenter une étape de calcul qui
est composable avec la suivante.
Deuxièmement, on va faire le traitement du format FASTA. En fait, les fichiers du FASTA
contiennent l’information du génome. Dans cette étape, au lieu de charger toute la
séquence, celle-ci sera lue morceau par morceau. Par conséquent, il faut adapter le
parsing fichier pour la rendre incrémentale.
La troisième étape de l’approche est l’étude de la représentation des flux de données et
comment ils permettent de réutilisation les briques de calcul.
Enfin et surtout, nous terminerons par l’application de l’algorithme de recherche aux
données réelles à l’aide du flux et de la communication avec le fichier.
Les trois premières étapes peuvent être initiées indépendamment afin de contribuer à
la dernière. En fait, les tâches seront réalisées des plus faciles aux plus difficiles. Ces
détails seront abordés plus tard.
2.3. Formats des données
Dans cette partie, le fichier FASTA est considéré comme le format d’entrée et le fichier
BED est considéré comme le format sortie. En effet, la lecture et l’écriture au fur et à
mesure seront mises à disposition.
7
2.3.1. Traitement de fichier FASTA
En fait, les séquences de génome sont distribuées sous format FASTA, qui
permet de représenter un ensemble de séquences (une séquence par
chromosome pour les génomes).
Présentons tout d’abord le format du fichier FASTA.
Un fichier FASTA est une liste d’items et chaque item a deux composants :
description et séquence. Les descriptions sont courtes et commencent par le
signe ">". Les séquences de données sont constituées des lettres
représentant les acides nucléiques ou les acides aminés de la séquence. Par
contre, chaque ligne contient au plus de 120 lettres. Donc, les séquences de
taille plus grande doivent être découpée en plusieurs lignes. Voyons un
exemple du fichier FASTA :
Un type naturel pour représenter le fichier FASTA est décrit comme suit :
type fasta = item list
and item = {
description : string ;
sequence : string ;
}
Par contre, avec ce type-là, on a besoin de charger en mémoire tout le
contenu du fichier. Or la séquence est parfois très longue. Donc, cette solution
conduit aussi la surcharge de l’espace mémoire. On doit ainsi traiter le fichier
en manière plus compliquée.
L’idée est de construire un nouveau type de donnée appelé «fasta_item».
Cette construction va stocker la séquence en plusieurs morceaux. Ce type
peut se décrire comme suit :
type fasta_item =
| Description of string
| Partial_sequence of string
>SEQUENCE_1
MTEITAAMVKELRESTGAGMMVAKNDQFIASRKQLSDQLLREKGLGKAAKKADRLAAEG
LVSVKVSDDFTIAAMRPSYLSYEDLDMTFVENEYLRESTGAGEKENEERRRLKDPNKPEHK
IPQFASRKQLSDAILKLRESTGAGKEELKAQVAKNDVVIAAACDSAEVFIADNSQLDSKLTL
MGQFYVMDDKKTVEQVIAEKEKEFGGKIKIVEFICFEVGEGLEKKTEDFAARLAAEG
>SEQUENCE_2
SATVSEINSETDFVARPSYLSYRPSYLSYHIQSSAEVASKSRELHSKAGSTINGVKFEEYLKSQI
ATIGENLVVRRFATLKAGANGVVNGYIHTNGRVGVVIAAACDSAEVASKSRDLLRQICMH
8
Le fichier FASTA est complètement traité. Par contre, le résultat reste
toujours les morceaux d’informations. C’est la raison pour laquelle, on a
besoin de représenter les données sous forme des flux.
2.3.2. Traitement de fichier BED
Pour sauvegarder le résultat de la recherche, nous utilisons le fichier BED. Le
fichier BED est un format utilisé en bioinformatique pour stocker les positions
dans un génome.
Le fichier BED a plusieurs colonnes. Ces colonnes contiennent les types
différents de données et sont séparées par les espaces ou les tabulations.
Voyons un exemple du fichier BED :
Dans le cadre du stage, les principales informations qu’on va stocker dans ce
fichier sont : description (nom des chromosomes), première position
d’occurrence, dernière position d’occurrence. Donc, un type représentant le
fichier BED peut être décrit comme suit :
type bed = bed_item list
and bed_item = {
chromosome : string ;
first_pos : int ;
last_pos: int;
}
Les items sont en petite taille. Donc, on n’a pas besoin de les décomposer
dans les algorithmes incrémentaux.
2.4. Algorithmes de recherche
Trois algorithmes seront abordés. L’implémentation de l’algorithme de Brute – Force se
fait en premier en raison de sa simplicité. Un changement sera mis en place pour que cet
algorithme s’adapte aux flux de données. Puis, l’algorithme de KMP sera réalisé. Enfin,
un algorithme de recherche étudié sera appliqué sur le motif de PWM. Dans chaque
implémentation, la complexité de l’algorithme sera considérée. L’impact du style
fonctionnel sur les performances sera aussi étudié. Ce fait me permet de me familiariser
avec le langage et m’habituer à l’évaluation des performances.
chr4 117471120 117472332
chr7 127472380 127473430
chr9 157473570 157474625
9
2.4.1. Algorithme naïf (algorithme de Brute Force)
Dans un premier temps, j’ai commencé résoudre le problème avec
l’algorithme naïf de recherche d’un motif dans une chaine de caractères
entrée au clavier. Par rapport à l’idée originale du Brute Force, une petite
différence est que la recherche va continuer même quand une occurrence est
trouvée. C’est-à-dire, la recherche va terminer si et seulement si on est à la
fin de la chaine de caractères. La valeur de retour est une liste des
occurrences du motif dans la chaine donnée.
Cet algorithme est réalisé en style fonctionnel même en style impératif pour
qu’on puisse faire le benchmark.
Est-ce que l’algorithme naïf peut jouer le rôle de l’algorithme de recherche
qu’on est en train de chercher ? Est-ce que cet algorithme peut s’adapter aux
données entrées morceau par morceau ? On va trouver la réponse dans les
parties suivantes.
2.4.2. Lecture de chaque caractère du fichier
En fait, l’opération élémentaire de l’algorithme de recherche est la
comparaison entre les caractères. Donc, au lieu d’occuper un caractère par
son indice dans une chaine, on va l’occuper par sa position dans le fichier.
Maintenant, cette position ainsi joue le rôle de l’indice de l’algorithme
original. Précisément, quand l’indice augmente, on fait augmenter la
position ; quand l’indice saute en arrière, la position est diminuée.
Cependant, quand on doit traiter les données venant d’un flux, par exemple :
un pipeline, on ne peut pas faire sauter la position en arrière. On ne peut rien
faire d’autre qu’avancer. Comment on le résout ?
2.4.3. Construction d’une table d’états
Une table d’états peut résoudre notre problème.
On a trouvé que quand on occupe un nouveau caractère, le résultat potentiel
de la recherche – succès ou échec – dépend partiellement du résultat de la
recherche au moment où on occupe le précédent caractère. En effet, la
recherche est potentiellement réussie si les comparaisons des k couples de
caractères (un du motif, un du fichier) sont tous vrais. Si k = n, où n est la taille
du motif, la recherche est certainement réussie. Au contraire, si la
comparaison au i-ième couple est échouée, la recherche est également
échouée. Autrement dit, au moment où on reçoit un nouveau caractère, le
10
résultat temporaire de la recherche est la conjonction du résultat temporaire
précédent et la comparaison d’un nouveau couple de caractères. Par
conséquent, la recherche est toujours temporairement échouée jusqu’au
moment la n-ième couple de caractères se correspond.
La théorie de l’automate peut décrire exactement cet algorithme : un état
change à l’état suivant grâce au caractère donné ; un mot est accepté s’il
accède à l’état final. Précisément, on considère l’exemple suivant.
En choisissant le motif «ABC», on a le graphe de transition :
Supposons une chaine «ABABC» envoyée caractère par caractère. Les tables
sont donc :
Ne recevoir rien vrai faux faux faux  échec
Recevoir «A» vrai vrai faux faux  échec
Recevoir «B» vrai vrai vrai faux  échec
Recevoir «A» vrai vrai faux faux  échec
Recevoir «B» vrai faux vrai faux  échec
Recevoir «C» vrai faux vrai faux  succès
La table peut également résoudre notre problème. Par contre, cet algorithme
semble insatisfaisant en raison de sa complexité. En effet, on souhaite avoir
une complexité plus optimiste. Heureusement, on a un autre algorithme qui
a une meilleure complexité et ne fait rien d’autre qu’avancer. Cet algorithme
sera présenté dans la partie suivante.
2.4.4. Algorithme de KMP
L’algorithme de KMP profite des résultats des itérations précédentes pour
faire diminuer le nombre d’itérations inutiles. Cet algorithme améliore la
complexité du programme en cas moyen. En effet, si la duplication des
caractères dans le motif est souvent, on n’a pas besoin de faire la recherche
0 1 2 3
3
*
*
*
*
A B C
11
caractère par caractère. Comme l’algorithme naïf, celui de KMP est
implémenté en style fonctionnel et en style impératif.
2.4.5. Recherche d’un motif de PWM
En biologie, une matrice poids-position (PWM) est souvent utilisée pour
représenter un motif, en particulier ceux qui représentent les sites de fixation
des protéines à l’ADN. PWM permet de représenter la préférence pour
certain nucléotide à chaque position du motif. Un PWM comprend 4 lignes
pour 4 types de nucléotide. Chaque ligne a n colonnes, où n est la longueur
des séquences de DNA. La création d’un PWM est décrite comme suit.
À partir d’une collection de séquences d’ADN, on va créer une matrice de
fréquence de position en calculant les occurrences de chaque nucléotide à
chaque position. Puis, un PWM se crée par le calcul de la probabilité des
occurrences de chaque nucléotide dans la séquence.
Précisément, on va considérer l’exemple suivant :
Supposons des séquences de DNA :
GAGGTATAC
TCCGTAAGC
CAGGTTGGA
ACAGTCAGT
AAGGTTATT
TAGGTACGG
ATGCTGACG
CTAGTATAG
TGTCTGAGC
AAGGTAAGT
Supposons qu’on connait les instances du motif, on a ainsi la matrice de
fréquence correspondante :
A 4 5 2 0 0 5 6 2 1
M = C 2 2 1 2 0 1 1 1 3
G 1 1 6 8 0 2 1 6 3
T 3 2 1 0 10 2 2 1 3
12
On a donc le PWM :
A 0.4 0.5 0.2 0 0 0.5 0.6 0.2 0.1
M = C 0.2 0.2 0.1 0.2 0 0.1 0.1 0.1 0.3
G 0.1 0.1 0.6 0.8 0 0.2 0.1 0.6 0.3
T 0.3 0.2 0.1 0 1 0.2 0.2 0.1 0.3
Dans la réalité, les éléments du PWM sont souvent transformés en formule :
Mj,k = ln (Mj,k/0.25)
La recherche des occurrences des PWM est analogue à algorithme naïf sauf
qu’au lieu de comparer un caractère de la séquence à un caractère du motif,
on va regarder le score du caractère de la séquence dans la matrice. Ces scores
sont additionnés et on cherche les positions où les sommes dépassent un
certain seuil.
2.5. Représentation des flux
Le traitement en espace mémoire constant fait partie aussi dans cette session. En fait, je
vais créer une représentation des flux de données. Le flux soit se participe dans le progrès
de recherche, soit consomme et produit les items de la recherche.
2.5.1. Le type «générateur»
L’idée ici est de représenter un processus qui produit une nouvelle donnée
quand on lui demande. En particulier, la définition du type est écrite :
type α gen = unit -> α option
Le type option est utilisé pour représenter naturellement le résultat de la
recherche : soit rien, soit quelque chose d’un type quelconque. Plus
précisément, on va obtenir None si la séquence est terminée, ou Some x (x
est du type α) si le générateur a encore des éléments.
En utilisant ce type, le parsing du fichier FASTA est décrit comme un processus
qui prend un générateur des chaines de caractères et retourne un générateur
des items fasta. Plus précisément, ce processus est décrit :
val fasta_parser : string gen -> fasta_item gen
13
Ce type nous permet de représenter la recherche du motif comme un
processus qui prend un générateur des chaines de caractères et retourne un
générateur des entiers. Ce processus peut être décrit :
val search_motif : string -> string gen -> int gen
La recherche du motif dans un fichier FASTA est en fait la composition de ces
deux processus. Donc, cette composition est un processus qui prend un
générateur des items fasta et retourne un générateur des chaines de
caractères et des entiers. Chaque chaine de caractère représente le nom de
chromosome où on trouve l’occurrence. La recherche du motif dans un fichier
est décrite :
val search_motif_in_fasta : string -> fasta_item gen -> (string * int) gen
En fait, cette solution n’est pas une idéale. En effet, les fonctions doivent
s’écrire partiellement impérativement. Donc, cela n’empêche pas totalement
les effets de bord.
2.5.2. La continuation
Ici on voit un flux comme un processus consommant et produisant des items
et on représente son état et les états suivant à l’aide d’une continuation.
Cette idée peut se décrire comme suit :
type (‘i, ‘o) pipe =
| Need_input of ‘i option -> (‘i, ‘o) pipe
| Has_output of ‘o * (‘i, ‘o) pipe
| Done
Avec ce type-là, le parsing du fichier FASTA est décrit :
val fasta_parser : (string, fasta_item) pipe
Ensuite, la recherche du motif est représentée comme :
val search_motif : string -> (string, int) pipe
La composition de ces deux processus est:
val string -> (string, string * int) pipe
14
On aura aussi un opérateur de composition, qui peut être décrit:
($$): (α, β) pipe -> (β, γ) pipe -> (α, γ) pipe
Cependant, cette idée est difficile à réaliser. Dans le cas où on ne peut pas
l’implémenter, la première idée est acceptable.
2.6. Application aux données
On va appliquer l’algorithme de recherche d’un PWM au génome de la souris. On va
d’abord télécharger le génome de souris sous la forme d’un fichier FASTA, puis envoyer
cette information sous forme d’un flux à la recherche du motif, ensuite obtenir un flux
de résultats et enfin l’écrire dans un fichier BED. Il s’agira de comparer ces positions à
celles déterminées expérimentalement et de calculer les performances de prédictions.
2.7. Travaux réalisés et leurs résultats
Pendant 1.5 mois, j’ai terminé une moitié du projet. En fait, j’ai implémenté l’algorithme
de Brute-Force et l’algorithme de KMP en deux styles : fonctionnel et impérative. J’ai fait
le benchmark pour évaluer leur performance. En plus, une table d’états est implémentée
pour optimiser la performance de l’algorithme naïf.
Le traitement du format FASTA est aussi réalisé. J’ai fait la lecture du fichier FASTA et
stocké les données morceau par morceau.
Enfin, j’ai travaillé sur la représentation les flux de données. J’ai réussi avec le type
«générateur». Maintenant, je suis en train de travailler sur la deuxième idée.
2.7.1. Benchmark d’algorithme naïf et de KMP
Dans le premier temps, le motif cherché et la chaine de caractères sont
générés à partir des caractères alphabétiques.
Dans le résultat obtenu, l’algorithme naïf du style fonctionnel performe
mieux que celui du style impératif. Ce fait se produit aussi dans le cas de
l’algorithme de KMP.
En comparant les deux algorithmes, le temps d’exécution de l’algorithme naïf
est plus court que celui de l’autre algorithme. Ce résultat est donc
raisonnable. En fait, en profitant des caractères analogues dans le motif,
15
l’algorithme fait diminuer le nombre total d’itérations. Donc, sa complexité
en moyen est baissée. Cependant, dans notre benchmark, les caractères du
motif sont générés parmi vingtaine caractères alphabétiques. Donc, peu
répétitions des caractères se produit. L’algorithme de KMP ne peut pas ainsi
réduire les itérations mais couter plus de temps pour créer son tableau. Dans
ce test, cet algorithme est en pire cas.
Avec un motif biologique, l’alphabet est beaucoup réduit et l’algorithme peut
mieux tirer parti de la table. J’ai fait un changement pour que le motif et la
chaine de caractères soient générés à partir de deux caractères. L’algorithme
de KMP est alors plus efficace que l’autre.
2.7.2. Construction la table d’états
On va initialiser une table de booléens de taille n + 1, où n est la taille du motif.
Tous les éléments sauf le premier de la table sont faux. Cela est la table
d’états quand on ne reçoit pas encore de caractère. Les tables suivantes sont
construites :
procédure contruire_table (donnée-résultat tab : tableau[0…n+1] de
booléens, donnée motif : chaine de caractères, donnée c: caractère)
{précondition : la précédente table initialisée, le motif de recherche et un
caractère données}
{post condition : la table d’états quand on reçoit le i-ième caractère}
variables
j : entier
n : entier
début
n <- taille_motif (motif)
pour j de n à 0 faire
tab.(j) <- tab.(j - 1) ^ (comparaison (c, motif.(j)))
fin pour
fin
Certainement, la procédure ci-dessus s’écrit fonctionnellement. Ainsi, cette
fonction marche bien avec l’algorithme naïf.
Malheureusement, cette fonction est parfois inutile. Le problème se produit
quand on veut imprimer exactement la chaine de caractères où on trouve le
motif. En fait, dans la biologie, quelques fois, peu différence dans les deux
chaines de caractères est acceptable. Dans ce cas, on veut savoir
16
explicitement la sous-chaine qui conduit à la correspondance. Donc, une table
contient seulement l’état précédent ne suffit pas.
2.7.3. Lecture de fichier FASTA
Ici, je vais aborder la lecture du fichier FASTA.
Le fichier va être lu morceau par morceau. À chaque lecture, chaque caractère
doit être considéré si cela appartient d’une description ou d’une séquence.
En effet, la plupart des «>» représente une description. En plus, j’ai un
drapeau pour signaler l’état (description ou séquence) du flux de données à
chaque moment du parcours. Je stocke aussi une position qui marque le
commencement de la description ou de la séquence. Si l’état du flux va être
changé, c’est-à-dire une description (ou une séquence) est terminé, le
précédent caractère ainsi appartient d’une séquence (ou une description), je
sauvegarde tout ce que j’ai déjà parcouru à partir de la position de
commence, puis je réinitialise la valeur de la position marquée.
Si la chaine de caractères est terminée mais aucun signal de changement
l’état n’est trouvé, je sauvegarde la chaine de caractères et change la position
marquée sans faire changer l’état du flux. En effet, on a accepté que la
séquence de DNA peut être sauvegarde en plusieurs morceaux, parce qu’elle
est très longue. Par contre, le commentaire découpé peut se concaténer.
Parce qu’il est court, le cout de concaténation est bas.
2.8. Travaux prévus
Dans la deuxième partie du stage, les tâches suivantes seront faites :
- la deuxième représentation du flux ;
- l’écriture dans le fichier BED ;
- l’implémentation de l’algorithme de recherche sur le motif de PWM ;
- l’intégration des fonctions produites ;
- la documentation.
17
3. Retour sur expérience
Dans cette partie, je vais indiquer les expériences que j’ai acquises pendant le stage. J’aborde
en premier mes connaissances sur la programmation fonctionnelle et sur le langage OCaml. Je
vais parler ensuite les autres compétences techniques que j’ai renforcées. Cette partie sera
terminée par mes expériences professionnelles.
3.1. Apprentissage du langage OCaml et de la programmation fonctionnelle
Après l’apprentissage, je peux répondre aux questions suivantes :
Qu’est-ce que la programmation fonctionnelle ? Pourquoi utilise-t-on ce paradigme ?
Quelles sont ses propriétés ? Pourquoi utilise-t-on l’OCaml ? Quels sont les avantages du
langage ?
3.1.1. Programmation fonctionnelle
La programmation fonctionnelle est un paradigme moderne de
programmation dont l’unité de base est la fonction. C’est-à-dire, ce
paradigme de programmation se base sur l’application de fonctions
mathématiques. En fait, l’utilisateur donne l’expression ou la déclaration.
Ainsi, le programme en profite pour évaluer les fonctions pour retourner le
résultat correspondant.
La programmation fonctionnelle ne permet pas la modification des variables
ni le changement d’états. C’est la différence principale entre ce paradigme
avec les autres, qui utilisent souvent les phrases impératives pour guider le
programme en un ou plusieurs étapes obtenir le résultat.
La programmation fonctionnelle possède plusieurs atouts. D’abord, la gestion
d’espace mémoire se lance automatiquement. L’utilisateur donc n’a pas
soucis du tas, de la pile et d’allocation de l’espace mémoire.
Ensuite, l’utilisateur peut facilement prendre une fonction comme argument
sans l’utilisation d’un pointeur de fonction (comme d’autres paradigmes). Cet
avantage sera représenté plus clair dans les parties suivantes.
La programmation fonctionnelle est aussi très utile pour les callbacks (utilisés
dans les IHMs pour les boucles d'évènements) et très excellente pour
exprimer des algorithmes génériques. Beaucoup de fonctions peuvent être
appliquées avec n'importe quel type de données. Par exemple, la fonction
List.map peut s’utiliser avec les listes des entiers, des caractères, des listes,
etc.
18
Les contraintes du sujet sont la dernière raison pour laquelle on choisit la
programmation fonctionnelle. En effet, la plupart des fonctions de ce
paradigme n’a aucun effet de bord. Donc, on peut utiliser les fonctions sans
soucis d’une sorte d'état caché gardée.
3.1.2. Langage OCaml
OCaml est un langage de programmation fonctionnelle. Dans l’OCaml, les
types de données sont synthétisés et gérés par l’ordinateur. Le logiciel OCaml
est offert et utilisable sur presque tous plate-forme Unix et Windows.
Outre les avantages d’un langage de programmation fonctionnelle, OCaml
garde aussi ses propres atouts. D’abord, il autorise les effets de bord au
travers des références et des tableaux. OCaml est donc plus pratique parce
que les fonctions avec une sorte d’état caché gardée sont parfois utile.
Un autre avantage de l’OCaml est son évaluation stricte. Cette caractéristique
rend ainsi un résultat plus raisonnable. Par contre, si l’utilisateur veut faire
quelques choses bizarres, l’OCaml ne l’empêche pas.
Enfin et surtout, l’OCaml attire une base d’utilisateurs importante grâce aux
ses services hors de la programmation fonctionnelle. En effet, l’OCaml est
très apprécié grâce au service de la programmation orientée d’objet et du
système de type.
3.2. Compétences techniques acquises en OCaml
Dans la programmation fonctionnelle, particulièrement dans le langage OCaml, il y a des
nouvelles notions que nous utilisons rarement dans d’autres paradigmes et langages. En
fait, quelques notions sont déjà connues. Par contre, je vais les montrer aussi dans cette
partie, parce qu’elles sont les plus importantes et très utiles.
3.2.1. Variable non modifiable
Dans la programmation fonctionnelle, surtout l’OCaml, on ne peut pas
modifier les variables. Elles sont donc gérées automatiquement. Cela nous
semble un massif avantage. Par contre, pour profiter cet atout, nous devons
donner une valeur à chaque fois une variable initialisée. Cette propriété est
donc contraire avec notre habitude : initialiser une variable, puis affecter sa
valeur.
19
Par exemple, une variable peut être initialisée :
# let x = 5;;
val x : int = 5
ou
# let y = String.length “ABCD”;;
val y: int = 4
Par contre, on ne peut pas écrire comme suivant :
# let z ;;
3.2.2. Typage fort
L’OCaml est un langage de typage fort. C’est-à-dire, les variables utilisées a
totalement et seulement un type. Donc, les conversions implicites de types
sont formellement interdites.
On a un exemple :
# let double x = x * 2 ;;
val double : int -> int
Dans cet exemple, on va multiplier un entier avec 2. Dans d’autres langages,
si l’on veut la multiplication d’un nombre réel, on peut également utiliser
cette fonction. Par contre, dans l’OCaml, cela n’est permis pas. On doit ainsi
écrire une autre fonction :
# let double_reel x = x *. 2. ;;
val double_reel : float -> float
On va appliquer la fonction ci-dessus :
# double_reel 1. ;;
:- float = 2.
Si l’on écrit :
# double_reel 2 ;;
cela va rend une erreur.
20
3.2.3. Inférence de type
Grâce au typage fort, on a un mécanisme de trouver le type de la fonction.
Ainsi, le compilateur trouve simplement le type le plus général qui puisse
prendre l'expression. Par conséquent, une fonction ne s’exécute pas une fois
que son type soit inapproprié.
Supposons l’exemple suivant :
# let isvowel c =
match c with
‘a’ -> true
| ‘e’ -> true
| ‘i’ -> true
| ‘o’ -> true
| ‘u’ -> true
| _ -> false
;;
val isvowel : char -> bool
Cette fonction va prendre un caractère et retourner vrai si c’est une voyelle.
Si l’on écrit :
# isvowel ‘’a’’;;
cela va rendre une erreur, parce que ‘’a’’ n’est pas du type char. Pour évaluer
le caractère a, on doit le mettre entre les apostrophes.
3.2.4. Fonction d’ordre supérieur
Dans programmation fonctionnelle, on se permet d’utiliser une fonction en
tant qu’argument dans une autre fonction. La fonction qui prend une autre
fonction comme son argument est la fonction d’ordre supérieur.
On a un exemple suivant :
# let double ls = List.map ( fun x -> x * 2 ) ls ;;
val double : int list -> (int -> int) -> int list
# double [1 ; 2 ; 3] ;;
:- int list = [2 ; 4 ; 6]
Dans cet exemple, la fonction (fun x -> x * 2) prend un argument x et retourne
x * 2. J’ai appliqué cette fonction pour tous les éléments d’une liste d’entiers
21
par l’appel de la fonction map. C’est-à-dire, la fonction map prend la fonction
(fun x -> x * 2) comme argument. Donc, la fonction map s’appele une fonction
d'ordre supérieur.
3.2.5. Fonction récursive et fonction récursive terminale
Une fonction est récursive si cette fonction utilise lui-même pour obtenir le
résultat. La fonction suivant est récursive :
# let rec length l =
match l with
[] -> 0
|_ ::t -> 1 + length t
Une fonction récursive est dit terminale si le résultat du dernier appel de cette
fonction rend exactement la valeur de retour du programme.
On a aussi la version récursive terminale de la fonction ci-dessus comme
suivant :
# let rec length_inner l n =
match l with
[] -> n
| _ ::t -> length_inner t (n + 1)
# let rec length l = length_inner l 0
Dans programmation fonctionnelle, on utilise souvent les fonctions
récursives. La complexité de la fonction récursive est meilleure que celle non-
récursive. Cependant, grâce à la comparaison ci-dessus, les fonctions
récursives terminales sont les plus appréciées dans OCaml.
3.2.6. Programmation impérative dans OCaml
Comme on le sait, la programmation fonctionnelle ne permet pas de changer
les variables. Par contre, les utilisateurs d’OCaml peuvent également écrire
impérativement leurs programmes.
L’OCaml fournit une construction s’appelé référence qui est une boite dans
laquelle on peut stocker une valeur. Nous avons un exemple suivant :
22
# let x = ref 0 ;;
val x : int ref = {contents = 0}
Pour extraire le contenu courant d’une référence, on utilise l’opérateur «!»,
comme l’exemple suivant :
# let p = !x ;;
val p : int = 0
Pour mettre à jour le contenu d’un référence, on utilise l’opérateur «:=».
# x := 50 ;;
- : unit = ()
Une nouvelle valeur est mise dans la variable x, puis rien n’est retourné. Le
type unit est ainsi pour but de représenter «rien».
3.2.7. Type optionnel
Dans réalité, nous avons d’un type qui représente soit rien, soit quelque
chose du type «α». Donc, l’OCaml nous permet de définir ce type comme
suivant :
# type ‘a option = None | Some of ‘a ;;
type ‘a option = None | Some of ‘a
Un exemple de l’utilisation de ce type:
# let nothing = None ;;
val nothing : ‘a option = None
# let number = Some 50;;
val number: int option = Some 50
3.2.8. Application partielle
On a une fonction comme suivant :
# let plus x y = x + y ;;
val plus : int –> int -> int
Du côté informatique, cette fonction est une fonction qui prend deux
arguments x et y et retourne x + y.
23
Du côté mathématique, cette fonction est une fonction qui prend un entier x
et renvoie une fonction qui si l’on lui donne un entier y, retournera entier x +
y. Cette fonction peut également être écrite comme suivant :
# let plus x = fun y -> x + y ;;
val plus : int -> (int -> int)
La partie (int -> int) peut ainsi décrite comme une fonction qui prend un entier
et retourne un entier. Donc, si l’on écrit :
# let f = plus 1 ;;
val f : int -> int
on a surement une fonction qui prend un entier et retourne son successeur.
La fonction f est appelé une application partielle de la fonction plus, parce f a
appliqué la fonction plus avec 1 et retourne une fonction du type int -> int.
3.3. Autres compétences techniques
Outre la technique de programmation fonctionnelle, j’améliore ma compétence d’analyse
de la complexité. En effet, beaucoup d’éléments contribuent à la performance d’un
programme : algorithme, codage, système d’exploitation, etc. Donc, pour évaluer un
programme, il me faut remarquer plusieurs aspects.
Je me renforce aussi en la communication avec le fichier. En effet, cette mission me donne
une grande occasion pour faire la lecture et l’écriture dans les fichiers.
Ce stage est aussi un cas concret dont j’utilise la connaissance de l’automate et des
mathématiques. En fait, les fonctions mathématiques sont continument utilisées pendant ce
stage. Beaucoup de problèmes peuvent donc être résolus de manière fonctionnelle.
J’ai aussi l’occasion d’utiliser le GIT, un gestionnaire des versions du code.
Outre la connaissance de base, mon tuteur m’aide beaucoup sur le mécanisme de diviser
pour régner et me donne une vue générale du problème. Beaucoup de problèmes de grande
taille peuvent se résoudre en plusieurs sous-problèmes analogues. Cette technique est très
importante pour la construction des grands programmes.
3.4. Côté professionnel
J’ai l’environnement pour pratiquer mon français. En plus, j’ai beaucoup de chances de
discussion et d’explication avec mon tuteur sur le thème scientifique. Le niveau de mon sens
critique est aussi meilleur.
24
Conclusion
Après une moitié du stage, quelques conclusions peuvent être tirées.
D’abord, le stage au sein de PRABI me permet de découvrir cette organisation et son
fonctionnement. En effet, les centres de recherche comme PRABI sont motivés par le
développement de la science. En outre, je comprends bien la connexion étroite entre les
composants de recherche. En effet, un projet de recherche doit être travaillé par plusieurs équipes.
C’est la raison pour laquelle les grands changements se produisent.
Ensuite, travailler dans un département de bioinformatique m’aide à trouver l’harmonie entre deux
domaines scientifiques : biologie et informatique. En fait, l’informatique valorise des activités de
recherche biologique. En reverse, la biologie crée l’environnement pratique pour que les
innovations informatiques soient utilisées.
Après, pendant 1.5 mois, j’ai appris un nouveau paradigme de programmation et un langage
moderne. En outre, j’ai travaillé partiellement sur le sujet de recherche d’un motif en espace
mémoire constant dans un génome. Mon travail contribue ainsi une petite partie à la recherche
scientifique, surtout la recherche en bioinformatique.
Ce stage m’aide aussi à renforcer mes savoirs théoriques. En effet, j’améliore ma compétence
d’analyse de la complexité. En plus, je maitrise de la technique de lecture et d’écriture des fichiers.
En outre, j’ai un nouveau point de vue sur l’usage de l’automate. Ainsi, je connais bien le rôle de la
connaissance théorique et prends conscience des conditions réelles d'exercice.
Dernièrement, en travaillant hors de l’environnement informatique, je trouve aussi l’importance de
la technologie d’information dans la vie, particulièrement dans le domaine de recherche. Donc,
l’informatique est vraiment une évolution du monde. Comme tous les jeunes, j’aimerais bien
contribuer et m’attacher à ce domaine scientifique.
25
Bibliographie
1. Livre
WHITINGTON John. OCaml from the very beginning. Royaume-Uni. Coherent Press. 2013.
194 pages.
2. Sites internet consultés
https://fr.wikipedia.org/wiki/Programmation_fonctionnelle
https://ocaml.org/learn/tutorials/functional_programming.fr.html
http://caml.inria.fr/about/history.en.html
http://caml.inria.fr/resources/doc/faq/general.en.html
http://caml.inria.fr/pub/docs/manual-ocaml/libref/index.html
http://pbil.univ-lyon1.fr/members/veber/ocaml/fold.html
https://fr.wikipedia.org/wiki/FASTA_(format_de_fichier)
https://fr.wikipedia.org/wiki/BED_(format_de_fichier)
https://en.wikipedia.org/wiki/Position_weight_matrix
https://blogs.janestreet.com/core_bench-micro-benchmarking-for-ocaml/
https://github.com/c-cube/gen

Contenu connexe

Similaire à p1314558_NGUYEN-Thi-Tuong-Vy

Programmation en technologie (C.Blin)
Programmation en technologie (C.Blin)Programmation en technologie (C.Blin)
Programmation en technologie (C.Blin)APMEPIledeFrance
 
L'expérience "jeune professionnel", version 2
L'expérience "jeune professionnel", version 2L'expérience "jeune professionnel", version 2
L'expérience "jeune professionnel", version 2richard peirano
 
"jeune professionnel, un jeu de rôle
"jeune professionnel, un jeu de rôle"jeune professionnel, un jeu de rôle
"jeune professionnel, un jeu de rôlerichard peirano
 
Quels usages pour les jeux électroniques en classe ?
Quels usages pour les jeux électroniques en classe ?Quels usages pour les jeux électroniques en classe ?
Quels usages pour les jeux électroniques en classe ?Caroline Jouneau-Sion
 
Scénographie des controverses 2013 à Sciences Po, Ecole de la Com
Scénographie des controverses 2013 à Sciences Po, Ecole de la ComScénographie des controverses 2013 à Sciences Po, Ecole de la Com
Scénographie des controverses 2013 à Sciences Po, Ecole de la ComStéphane Rouilly
 
02 diaporama introduction_au_projet_robotique
02 diaporama introduction_au_projet_robotique02 diaporama introduction_au_projet_robotique
02 diaporama introduction_au_projet_robotiquedamira47
 
Nabil bouzerna - Mémoire d'ingénieur / Master Thesis ENSIIE - ADVESTIGO - 2007
Nabil bouzerna - Mémoire d'ingénieur / Master Thesis ENSIIE - ADVESTIGO - 2007  Nabil bouzerna - Mémoire d'ingénieur / Master Thesis ENSIIE - ADVESTIGO - 2007
Nabil bouzerna - Mémoire d'ingénieur / Master Thesis ENSIIE - ADVESTIGO - 2007 Nabil Bouzerna
 
Mémoire de Projet de Fin d’Etudes
Mémoire de Projet de Fin d’EtudesMémoire de Projet de Fin d’Etudes
Mémoire de Projet de Fin d’EtudesAicha OUALLA
 
Débuter dans la conception pédagogique et multimédia : De la réalisation à l...
Débuter dans la conception pédagogique et multimédia :  De la réalisation à l...Débuter dans la conception pédagogique et multimédia :  De la réalisation à l...
Débuter dans la conception pédagogique et multimédia : De la réalisation à l...Aurélie Malétras
 
Chikhi abdelouahab
Chikhi abdelouahabChikhi abdelouahab
Chikhi abdelouahabSadouk Ta
 
rapport_stage_issame
rapport_stage_issamerapport_stage_issame
rapport_stage_issameAMAL Issame
 
CIAN-David_Compte_Rendu_PP
CIAN-David_Compte_Rendu_PPCIAN-David_Compte_Rendu_PP
CIAN-David_Compte_Rendu_PPDavid Cian
 
Rapport Mini Projet : élaborer un moteur de Recherche spécialisé en Education
Rapport Mini Projet : élaborer un moteur de Recherche spécialisé en EducationRapport Mini Projet : élaborer un moteur de Recherche spécialisé en Education
Rapport Mini Projet : élaborer un moteur de Recherche spécialisé en EducationMohamed Amine Mahmoudi
 
Comprendre la planification de projets
Comprendre la planification de projetsComprendre la planification de projets
Comprendre la planification de projetsMichel Estève
 
Manuel ns1.3
Manuel ns1.3Manuel ns1.3
Manuel ns1.3thiedia
 
Plonegov projet egov
Plonegov projet egov Plonegov projet egov
Plonegov projet egov HORIYASOFT
 
OpenCR__Rapport_soutenance_Finale
OpenCR__Rapport_soutenance_FinaleOpenCR__Rapport_soutenance_Finale
OpenCR__Rapport_soutenance_FinaleChady Dimachkie
 
La pédagogie par projet assistée par Moodle
La pédagogie par projet assistée par MoodleLa pédagogie par projet assistée par Moodle
La pédagogie par projet assistée par MoodleJulien Morice
 

Similaire à p1314558_NGUYEN-Thi-Tuong-Vy (20)

Programmation en technologie (C.Blin)
Programmation en technologie (C.Blin)Programmation en technologie (C.Blin)
Programmation en technologie (C.Blin)
 
thesis
thesisthesis
thesis
 
L'expérience "jeune professionnel", version 2
L'expérience "jeune professionnel", version 2L'expérience "jeune professionnel", version 2
L'expérience "jeune professionnel", version 2
 
"jeune professionnel, un jeu de rôle
"jeune professionnel, un jeu de rôle"jeune professionnel, un jeu de rôle
"jeune professionnel, un jeu de rôle
 
Quels usages pour les jeux électroniques en classe ?
Quels usages pour les jeux électroniques en classe ?Quels usages pour les jeux électroniques en classe ?
Quels usages pour les jeux électroniques en classe ?
 
Scénographie des controverses 2013 à Sciences Po, Ecole de la Com
Scénographie des controverses 2013 à Sciences Po, Ecole de la ComScénographie des controverses 2013 à Sciences Po, Ecole de la Com
Scénographie des controverses 2013 à Sciences Po, Ecole de la Com
 
02 diaporama introduction_au_projet_robotique
02 diaporama introduction_au_projet_robotique02 diaporama introduction_au_projet_robotique
02 diaporama introduction_au_projet_robotique
 
Nabil bouzerna - Mémoire d'ingénieur / Master Thesis ENSIIE - ADVESTIGO - 2007
Nabil bouzerna - Mémoire d'ingénieur / Master Thesis ENSIIE - ADVESTIGO - 2007  Nabil bouzerna - Mémoire d'ingénieur / Master Thesis ENSIIE - ADVESTIGO - 2007
Nabil bouzerna - Mémoire d'ingénieur / Master Thesis ENSIIE - ADVESTIGO - 2007
 
Mémoire de Projet de Fin d’Etudes
Mémoire de Projet de Fin d’EtudesMémoire de Projet de Fin d’Etudes
Mémoire de Projet de Fin d’Etudes
 
Débuter dans la conception pédagogique et multimédia : De la réalisation à l...
Débuter dans la conception pédagogique et multimédia :  De la réalisation à l...Débuter dans la conception pédagogique et multimédia :  De la réalisation à l...
Débuter dans la conception pédagogique et multimédia : De la réalisation à l...
 
Chikhi abdelouahab
Chikhi abdelouahabChikhi abdelouahab
Chikhi abdelouahab
 
rapport_stage_issame
rapport_stage_issamerapport_stage_issame
rapport_stage_issame
 
CIAN-David_Compte_Rendu_PP
CIAN-David_Compte_Rendu_PPCIAN-David_Compte_Rendu_PP
CIAN-David_Compte_Rendu_PP
 
Tpe nguyen tien-thinh
Tpe nguyen tien-thinhTpe nguyen tien-thinh
Tpe nguyen tien-thinh
 
Rapport Mini Projet : élaborer un moteur de Recherche spécialisé en Education
Rapport Mini Projet : élaborer un moteur de Recherche spécialisé en EducationRapport Mini Projet : élaborer un moteur de Recherche spécialisé en Education
Rapport Mini Projet : élaborer un moteur de Recherche spécialisé en Education
 
Comprendre la planification de projets
Comprendre la planification de projetsComprendre la planification de projets
Comprendre la planification de projets
 
Manuel ns1.3
Manuel ns1.3Manuel ns1.3
Manuel ns1.3
 
Plonegov projet egov
Plonegov projet egov Plonegov projet egov
Plonegov projet egov
 
OpenCR__Rapport_soutenance_Finale
OpenCR__Rapport_soutenance_FinaleOpenCR__Rapport_soutenance_Finale
OpenCR__Rapport_soutenance_Finale
 
La pédagogie par projet assistée par Moodle
La pédagogie par projet assistée par MoodleLa pédagogie par projet assistée par Moodle
La pédagogie par projet assistée par Moodle
 

p1314558_NGUYEN-Thi-Tuong-Vy

  • 1. Pôle Rhône-Alpes de Bioinformatique Bâtiment Gregor Mendel Université Claude Bernard Lyon 1 16, rue Raphaël Dubois 69622 Villeurbanne Cedex RAPPORT DE STAGE RECHERCHE DES MOTIFS EN MÉMOIRE CONSTANTE DANS UN GÉNOME NGUYEN Thi Tuong Vy Licence 3 Informatique Année universitaire 2015 – 2016 Maître de stage : Philippe VEBER Ingénieur de recherche
  • 2. i Résumé Ce rapport résume mes travaux chez Pôle Rhône-Alpes de Bio-Informatique (PRABI) pendant la première moitié du stage et couvre mes missions prévues pendant le reste du stage. Dans un premier temps, je vous présente l’entreprise. Le sujet du stage et les missions réalisées seront étudiées dans la deuxième partie. À partir de là, je vais vous parler des résultats obtenus et vous citer les tâches précédentes pour résoudre le problème. Enfin et surtout, mon retour sur l’expérience sera montré. L’objectif principal du stage est d’implémenter un algorithme de recherche de motifs en mémoire constante dans un génome. Le langage principal utilisé est OCaml, un langage de programmation fonctionnelle. Les travaux sont réalisés essentiellement sur Debian GNU/Linux.
  • 3. ii Remerciements Je voudrais tout d’abord exprimer mes sincères remerciements et ma profonde gratitude à Monsieur Philippe VEBER, ingénieur de recherche chez PRABI et en même temps mon maître de stage, de son enthousiasme et patience, de son enseignement, de son suivi régulier et de ses conseils portés sur mes travaux. Je tiens aussi mes sincères reconnaissances à Monsieur Guy PERRIERE – Directeur du PRABI, de son aide pour mon intégration dans la vie professionnelle et de m’avoir montré une vision concrète de l’environnement de recherche scientifique.
  • 4. iii Sommaire Résumé................................................................................................................................................................i Remerciements...................................................................................................................................................ii Introduction....................................................................................................................................................... 1 1. Environnement professionnel ................................................................................................................... 2 1.1. Organisation du PRABI....................................................................................................................... 2 1.2. Mission du PRABI-AMSB.................................................................................................................... 3 2. Mission ...................................................................................................................................................... 4 2.1. Contexte et problématique du stage ................................................................................................ 4 2.1.1. Contexte .................................................................................................................................... 4 2.1.2. Problématique........................................................................................................................... 4 2.2. Approche ........................................................................................................................................... 6 2.3. Formats des données ........................................................................................................................ 6 2.3.1. Traitement de fichier FASTA...................................................................................................... 7 2.3.2. Traitement de fichier BED ......................................................................................................... 8 2.4. Algorithmes de recherche ................................................................................................................. 8 2.4.1. Algorithme naïf (algorithme de Brute Force) ............................................................................ 9 2.4.2. Lecture de chaque caractère du fichier..................................................................................... 9 2.4.3. Construction d’une table d’états............................................................................................... 9 2.4.4. Algorithme de KMP.................................................................................................................. 10 2.4.5. Recherche d’un motif de PWM ............................................................................................... 11 2.5. Représentation des flux................................................................................................................... 12 2.5.1. Le type «générateur» .............................................................................................................. 12 2.5.2. La continuation........................................................................................................................ 13 2.6. Application aux données ................................................................................................................. 14 2.7. Travaux réalisés et leurs résultats ................................................................................................... 14 2.7.1. Benchmark d’algorithme naïf et de KMP ................................................................................ 14 2.7.2. Construction la table d’états ................................................................................................... 15 2.7.3. Lecture de fichier FASTA.......................................................................................................... 16 2.8. Travaux prévus ................................................................................................................................ 16 3. Retour sur expérience ............................................................................................................................. 17 3.1. Apprentissage du langage OCaml et de la programmation fonctionnelle...................................... 17 3.1.1. Programmation fonctionnelle ................................................................................................. 17 3.1.2. Langage OCaml........................................................................................................................ 18 3.2. Compétences techniques acquises en OCaml................................................................................. 18 3.2.1. Variable non modifiable .......................................................................................................... 18
  • 5. iv 3.2.2. Typage fort............................................................................................................................... 19 3.2.3. Inférence de type..................................................................................................................... 20 3.2.4. Fonction d’ordre supérieur...................................................................................................... 20 3.2.5. Fonction récursive et fonction récursive terminale ................................................................ 21 3.2.6. Programmation impérative dans OCaml................................................................................. 21 3.2.7. Type optionnel......................................................................................................................... 22 3.2.8. Application partielle ................................................................................................................ 22 3.3. Autres compétences techniques..................................................................................................... 23 3.4. Côté professionnel........................................................................................................................... 23 Conclusion ....................................................................................................................................................... 24 Bibliographie.................................................................................................................................................... 25
  • 6. 1 Introduction Mon stage se déroule au Pôle Rhône-Alpes de Bio-Informatique (PRABI), qui est l’un des six centres régionaux membres du Réseau National des plateformes en Bioinformatique (ReNaBi). Le PRABI regroupe plusieurs structures de la région Rhône-Alpes, en particulier le PRABI-AMSB, plateforme Université Lyon 1, spécialisé dans l’analyse des données génomiques. Passionnée par la biologie, intéressée par la génomique et ayant l’intention de m’orienter vers la recherche, j’ai rejoint l’équipe du PRABI-AMSB pour découvrir les algorithmes spécifiques en bioinformatique. Mon sujet de stage porte sur la recherche de motifs dans un génome. Mon rôle est de modifier un algorithme connu pour qu’il réalise une recherche avec une complexité en espace ne dépendant pas de la taille de la séquence en entrée. L’implémentation doit suivre un style fonctionnel et être réalisée dans le langage OCaml. Pour réaliser cette mission de 3 mois, j’ai commencé par apprendre le langage OCaml pendant 3 semaines et je travaille depuis sur une implémentation. J’ai expérimenté plusieurs algorithmes en utilisant les différents styles de programmation en changeant la manière dont les données sont reçues et les résultats transmis. Aussi, j’évalue les performances pour illustrer les avantages de chaque approche. À la fin du stage, je vais faire l’intégration des fonctions produites à une bibliothèque et la documentation correspondante.
  • 7. 2 1. Environnement professionnel Dans cette partie, je vais vous présenter ma structure d’accueil, son organisation et ses missions. 1.1. Organisation du PRABI Le PRABI est une structure régionale qui est le groupement des structures de bioinformatique en Rhône-Alpes. Parmi celles-ci, la composante Analyse et Modélisation des Systèmes Biologiques (AMSB) est une plateforme de l’Université Claude Bernard Lyon 1 (UCBL1). Dirigée par M. Guy PERRIERE, cette équipe est constituée d’ingénieurs autour d’un besoin technique (bioinformatique). Au sein de l’UCBL1, le PRABI-AMSB est décrit dans le figure 2 : Figure 1. Organigramme du PRABI Figure 2. Organigramme de UCBL1
  • 8. 3 Sous l’encadrement de M. Philippe VEBER – un ingénieur du LBBE – je contribue au projet ANR ARREPRESS soutenu par l’Agence Nationale de la Recherche (ANR), et auquel le PRABI-AMSB participe. 1.2. Mission du PRABI-AMSB Le PRABI-AMSB est spécialisé dans l’analyse de données génomiques, notamment issues du séquençage haut-débit, et a pour but de : - réaliser des prestations d’analyse ou de développement logiciel en bioinformatique ; - proposer des formations ; - participer aux collaborations scientifiques sur appel à projet. Plus précisément, les applications du séquençage haut-débit comprennent : - l’assemblage de génome ; - la mesure de l’expression des gènes (RNA-seq) ; - la détection des liaisons entre l’ADN et les protéines (ChIP-seq). Figure 3. Organigramme du PRABI-AMSB
  • 9. 4 2. Mission Dans cette partie, je parle d’abord de la problématique du stage et de l’approche que j’ai suivie pour y répondre. Je vais montrer ensuite le résultat que j’ai obtenu jusqu’ici. 2.1. Contexte et problématique du stage 2.1.1. Contexte L’équipe du PRABI et ses collaborateurs travaillent sur le projet ANR ARREPRESS. Le but de ce projet est de déterminer les mécanismes de répression de l’activité des gènes liés à une protéine appelée récepteur à l’acide rétinoïque (RAR). Ainsi, ce projet concerne principalement les sites de fixation de RAR à l’ADN. Le problème est abordé de 2 façons : détermination expérimentale (ChIP-seq) et prédiction «in silico» sur la séquence du génome. On souhaite comparer les résultats de la deuxième approche avec ceux de la première, qui est plus fiable mais aussi plus coûteuse. 2.1.2. Problématique Pour la prédiction, le modèle le plus utilisé pour les sites de liaison ADN- protéine sur le génome est appelé matrice poids-position (en anglais : position weight matrix - PWM). Dans le cadre du stage, je dois réaliser l’implémentation d’un algorithme de recherche de motif en complexité mémoire indépendante de la taille de l’entrée et de la sortie. En particulier, le motif cherché est un PWM et la recherche se fait sur un génome complet de souris. D’une part, l’information du génome occupe un espace mémoire de 2,8 Go ; d’autre part, les motifs recherchés sont courts et peu spécifiques : beaucoup d’occurrences seront trouvées et si l’on n’a pas un mécanisme d’écriture au fur et à mesure, cela conduira toujours à un problème d’espace mémoire. Cela cause donc un problème de chargement mémoire du génome et stockage des résultats. Ce projet a deux autres contraintes. La première est d’exprimer le calcul sous la forme des briques réutilisables. Deuxièmement, c’est d’utiliser un style fonctionnel (c’est-à-dire limitant l’usage d’effets de bord) pour un traitement impliquant des entrées/sorties. Pour la première contrainte, nous avons besoin de factoriser chaque étape du traitement : lecture depuis une source de données, analyse syntaxique, recherche du motif, écriture de résultats. Par exemple, un programme idéal doit pouvoir faire la recherche en utilisant la même implémentation de l’algorithme depuis n’importe quelle source de données. Donc, il faut
  • 10. 5 certainement un mécanisme qui peut traiter un flux de chaines de caractères à partir d’une source quelconque. lecture flux réseau lecture fichier décompression parsing FASTA recherche motif autres algorithmes unparsing BED écriture fichier compression Figure 4. Les étapes du traitement type que l’on souhaite réaliser FASTA et BED sont des formats très utilisés en bioinformatique.
  • 11. 6 2.2. Approche Pour chaque traitement, il faut formuler une implémentation incrémentale, c’est-à-dire capable de : - travailler sur une entrée partielle ; - s’interrompre quand il n’y a plus assez d’entrée ; - reprendre lorsque les nouvelles entrées sont disponibles. Pour rendre les algorithmes incrémentaux, on cherche à introduire un état explicite du traitement. En raison de la problématique et ses contraintes, mon approche se divise en 4 grandes étapes. Premièrement, je travaille sur les algorithmes de recherche. Pour rendre les algorithmes incrémentaux, il me faut introduire un état explicite par l’approche de la programmation fonctionnelle. En effet, à chaque réception de la chaine de caractères, un état sera utilisé pour donner un résultat et un nouvel état. Cela va représenter une étape de calcul qui est composable avec la suivante. Deuxièmement, on va faire le traitement du format FASTA. En fait, les fichiers du FASTA contiennent l’information du génome. Dans cette étape, au lieu de charger toute la séquence, celle-ci sera lue morceau par morceau. Par conséquent, il faut adapter le parsing fichier pour la rendre incrémentale. La troisième étape de l’approche est l’étude de la représentation des flux de données et comment ils permettent de réutilisation les briques de calcul. Enfin et surtout, nous terminerons par l’application de l’algorithme de recherche aux données réelles à l’aide du flux et de la communication avec le fichier. Les trois premières étapes peuvent être initiées indépendamment afin de contribuer à la dernière. En fait, les tâches seront réalisées des plus faciles aux plus difficiles. Ces détails seront abordés plus tard. 2.3. Formats des données Dans cette partie, le fichier FASTA est considéré comme le format d’entrée et le fichier BED est considéré comme le format sortie. En effet, la lecture et l’écriture au fur et à mesure seront mises à disposition.
  • 12. 7 2.3.1. Traitement de fichier FASTA En fait, les séquences de génome sont distribuées sous format FASTA, qui permet de représenter un ensemble de séquences (une séquence par chromosome pour les génomes). Présentons tout d’abord le format du fichier FASTA. Un fichier FASTA est une liste d’items et chaque item a deux composants : description et séquence. Les descriptions sont courtes et commencent par le signe ">". Les séquences de données sont constituées des lettres représentant les acides nucléiques ou les acides aminés de la séquence. Par contre, chaque ligne contient au plus de 120 lettres. Donc, les séquences de taille plus grande doivent être découpée en plusieurs lignes. Voyons un exemple du fichier FASTA : Un type naturel pour représenter le fichier FASTA est décrit comme suit : type fasta = item list and item = { description : string ; sequence : string ; } Par contre, avec ce type-là, on a besoin de charger en mémoire tout le contenu du fichier. Or la séquence est parfois très longue. Donc, cette solution conduit aussi la surcharge de l’espace mémoire. On doit ainsi traiter le fichier en manière plus compliquée. L’idée est de construire un nouveau type de donnée appelé «fasta_item». Cette construction va stocker la séquence en plusieurs morceaux. Ce type peut se décrire comme suit : type fasta_item = | Description of string | Partial_sequence of string >SEQUENCE_1 MTEITAAMVKELRESTGAGMMVAKNDQFIASRKQLSDQLLREKGLGKAAKKADRLAAEG LVSVKVSDDFTIAAMRPSYLSYEDLDMTFVENEYLRESTGAGEKENEERRRLKDPNKPEHK IPQFASRKQLSDAILKLRESTGAGKEELKAQVAKNDVVIAAACDSAEVFIADNSQLDSKLTL MGQFYVMDDKKTVEQVIAEKEKEFGGKIKIVEFICFEVGEGLEKKTEDFAARLAAEG >SEQUENCE_2 SATVSEINSETDFVARPSYLSYRPSYLSYHIQSSAEVASKSRELHSKAGSTINGVKFEEYLKSQI ATIGENLVVRRFATLKAGANGVVNGYIHTNGRVGVVIAAACDSAEVASKSRDLLRQICMH
  • 13. 8 Le fichier FASTA est complètement traité. Par contre, le résultat reste toujours les morceaux d’informations. C’est la raison pour laquelle, on a besoin de représenter les données sous forme des flux. 2.3.2. Traitement de fichier BED Pour sauvegarder le résultat de la recherche, nous utilisons le fichier BED. Le fichier BED est un format utilisé en bioinformatique pour stocker les positions dans un génome. Le fichier BED a plusieurs colonnes. Ces colonnes contiennent les types différents de données et sont séparées par les espaces ou les tabulations. Voyons un exemple du fichier BED : Dans le cadre du stage, les principales informations qu’on va stocker dans ce fichier sont : description (nom des chromosomes), première position d’occurrence, dernière position d’occurrence. Donc, un type représentant le fichier BED peut être décrit comme suit : type bed = bed_item list and bed_item = { chromosome : string ; first_pos : int ; last_pos: int; } Les items sont en petite taille. Donc, on n’a pas besoin de les décomposer dans les algorithmes incrémentaux. 2.4. Algorithmes de recherche Trois algorithmes seront abordés. L’implémentation de l’algorithme de Brute – Force se fait en premier en raison de sa simplicité. Un changement sera mis en place pour que cet algorithme s’adapte aux flux de données. Puis, l’algorithme de KMP sera réalisé. Enfin, un algorithme de recherche étudié sera appliqué sur le motif de PWM. Dans chaque implémentation, la complexité de l’algorithme sera considérée. L’impact du style fonctionnel sur les performances sera aussi étudié. Ce fait me permet de me familiariser avec le langage et m’habituer à l’évaluation des performances. chr4 117471120 117472332 chr7 127472380 127473430 chr9 157473570 157474625
  • 14. 9 2.4.1. Algorithme naïf (algorithme de Brute Force) Dans un premier temps, j’ai commencé résoudre le problème avec l’algorithme naïf de recherche d’un motif dans une chaine de caractères entrée au clavier. Par rapport à l’idée originale du Brute Force, une petite différence est que la recherche va continuer même quand une occurrence est trouvée. C’est-à-dire, la recherche va terminer si et seulement si on est à la fin de la chaine de caractères. La valeur de retour est une liste des occurrences du motif dans la chaine donnée. Cet algorithme est réalisé en style fonctionnel même en style impératif pour qu’on puisse faire le benchmark. Est-ce que l’algorithme naïf peut jouer le rôle de l’algorithme de recherche qu’on est en train de chercher ? Est-ce que cet algorithme peut s’adapter aux données entrées morceau par morceau ? On va trouver la réponse dans les parties suivantes. 2.4.2. Lecture de chaque caractère du fichier En fait, l’opération élémentaire de l’algorithme de recherche est la comparaison entre les caractères. Donc, au lieu d’occuper un caractère par son indice dans une chaine, on va l’occuper par sa position dans le fichier. Maintenant, cette position ainsi joue le rôle de l’indice de l’algorithme original. Précisément, quand l’indice augmente, on fait augmenter la position ; quand l’indice saute en arrière, la position est diminuée. Cependant, quand on doit traiter les données venant d’un flux, par exemple : un pipeline, on ne peut pas faire sauter la position en arrière. On ne peut rien faire d’autre qu’avancer. Comment on le résout ? 2.4.3. Construction d’une table d’états Une table d’états peut résoudre notre problème. On a trouvé que quand on occupe un nouveau caractère, le résultat potentiel de la recherche – succès ou échec – dépend partiellement du résultat de la recherche au moment où on occupe le précédent caractère. En effet, la recherche est potentiellement réussie si les comparaisons des k couples de caractères (un du motif, un du fichier) sont tous vrais. Si k = n, où n est la taille du motif, la recherche est certainement réussie. Au contraire, si la comparaison au i-ième couple est échouée, la recherche est également échouée. Autrement dit, au moment où on reçoit un nouveau caractère, le
  • 15. 10 résultat temporaire de la recherche est la conjonction du résultat temporaire précédent et la comparaison d’un nouveau couple de caractères. Par conséquent, la recherche est toujours temporairement échouée jusqu’au moment la n-ième couple de caractères se correspond. La théorie de l’automate peut décrire exactement cet algorithme : un état change à l’état suivant grâce au caractère donné ; un mot est accepté s’il accède à l’état final. Précisément, on considère l’exemple suivant. En choisissant le motif «ABC», on a le graphe de transition : Supposons une chaine «ABABC» envoyée caractère par caractère. Les tables sont donc : Ne recevoir rien vrai faux faux faux  échec Recevoir «A» vrai vrai faux faux  échec Recevoir «B» vrai vrai vrai faux  échec Recevoir «A» vrai vrai faux faux  échec Recevoir «B» vrai faux vrai faux  échec Recevoir «C» vrai faux vrai faux  succès La table peut également résoudre notre problème. Par contre, cet algorithme semble insatisfaisant en raison de sa complexité. En effet, on souhaite avoir une complexité plus optimiste. Heureusement, on a un autre algorithme qui a une meilleure complexité et ne fait rien d’autre qu’avancer. Cet algorithme sera présenté dans la partie suivante. 2.4.4. Algorithme de KMP L’algorithme de KMP profite des résultats des itérations précédentes pour faire diminuer le nombre d’itérations inutiles. Cet algorithme améliore la complexité du programme en cas moyen. En effet, si la duplication des caractères dans le motif est souvent, on n’a pas besoin de faire la recherche 0 1 2 3 3 * * * * A B C
  • 16. 11 caractère par caractère. Comme l’algorithme naïf, celui de KMP est implémenté en style fonctionnel et en style impératif. 2.4.5. Recherche d’un motif de PWM En biologie, une matrice poids-position (PWM) est souvent utilisée pour représenter un motif, en particulier ceux qui représentent les sites de fixation des protéines à l’ADN. PWM permet de représenter la préférence pour certain nucléotide à chaque position du motif. Un PWM comprend 4 lignes pour 4 types de nucléotide. Chaque ligne a n colonnes, où n est la longueur des séquences de DNA. La création d’un PWM est décrite comme suit. À partir d’une collection de séquences d’ADN, on va créer une matrice de fréquence de position en calculant les occurrences de chaque nucléotide à chaque position. Puis, un PWM se crée par le calcul de la probabilité des occurrences de chaque nucléotide dans la séquence. Précisément, on va considérer l’exemple suivant : Supposons des séquences de DNA : GAGGTATAC TCCGTAAGC CAGGTTGGA ACAGTCAGT AAGGTTATT TAGGTACGG ATGCTGACG CTAGTATAG TGTCTGAGC AAGGTAAGT Supposons qu’on connait les instances du motif, on a ainsi la matrice de fréquence correspondante : A 4 5 2 0 0 5 6 2 1 M = C 2 2 1 2 0 1 1 1 3 G 1 1 6 8 0 2 1 6 3 T 3 2 1 0 10 2 2 1 3
  • 17. 12 On a donc le PWM : A 0.4 0.5 0.2 0 0 0.5 0.6 0.2 0.1 M = C 0.2 0.2 0.1 0.2 0 0.1 0.1 0.1 0.3 G 0.1 0.1 0.6 0.8 0 0.2 0.1 0.6 0.3 T 0.3 0.2 0.1 0 1 0.2 0.2 0.1 0.3 Dans la réalité, les éléments du PWM sont souvent transformés en formule : Mj,k = ln (Mj,k/0.25) La recherche des occurrences des PWM est analogue à algorithme naïf sauf qu’au lieu de comparer un caractère de la séquence à un caractère du motif, on va regarder le score du caractère de la séquence dans la matrice. Ces scores sont additionnés et on cherche les positions où les sommes dépassent un certain seuil. 2.5. Représentation des flux Le traitement en espace mémoire constant fait partie aussi dans cette session. En fait, je vais créer une représentation des flux de données. Le flux soit se participe dans le progrès de recherche, soit consomme et produit les items de la recherche. 2.5.1. Le type «générateur» L’idée ici est de représenter un processus qui produit une nouvelle donnée quand on lui demande. En particulier, la définition du type est écrite : type α gen = unit -> α option Le type option est utilisé pour représenter naturellement le résultat de la recherche : soit rien, soit quelque chose d’un type quelconque. Plus précisément, on va obtenir None si la séquence est terminée, ou Some x (x est du type α) si le générateur a encore des éléments. En utilisant ce type, le parsing du fichier FASTA est décrit comme un processus qui prend un générateur des chaines de caractères et retourne un générateur des items fasta. Plus précisément, ce processus est décrit : val fasta_parser : string gen -> fasta_item gen
  • 18. 13 Ce type nous permet de représenter la recherche du motif comme un processus qui prend un générateur des chaines de caractères et retourne un générateur des entiers. Ce processus peut être décrit : val search_motif : string -> string gen -> int gen La recherche du motif dans un fichier FASTA est en fait la composition de ces deux processus. Donc, cette composition est un processus qui prend un générateur des items fasta et retourne un générateur des chaines de caractères et des entiers. Chaque chaine de caractère représente le nom de chromosome où on trouve l’occurrence. La recherche du motif dans un fichier est décrite : val search_motif_in_fasta : string -> fasta_item gen -> (string * int) gen En fait, cette solution n’est pas une idéale. En effet, les fonctions doivent s’écrire partiellement impérativement. Donc, cela n’empêche pas totalement les effets de bord. 2.5.2. La continuation Ici on voit un flux comme un processus consommant et produisant des items et on représente son état et les états suivant à l’aide d’une continuation. Cette idée peut se décrire comme suit : type (‘i, ‘o) pipe = | Need_input of ‘i option -> (‘i, ‘o) pipe | Has_output of ‘o * (‘i, ‘o) pipe | Done Avec ce type-là, le parsing du fichier FASTA est décrit : val fasta_parser : (string, fasta_item) pipe Ensuite, la recherche du motif est représentée comme : val search_motif : string -> (string, int) pipe La composition de ces deux processus est: val string -> (string, string * int) pipe
  • 19. 14 On aura aussi un opérateur de composition, qui peut être décrit: ($$): (α, β) pipe -> (β, γ) pipe -> (α, γ) pipe Cependant, cette idée est difficile à réaliser. Dans le cas où on ne peut pas l’implémenter, la première idée est acceptable. 2.6. Application aux données On va appliquer l’algorithme de recherche d’un PWM au génome de la souris. On va d’abord télécharger le génome de souris sous la forme d’un fichier FASTA, puis envoyer cette information sous forme d’un flux à la recherche du motif, ensuite obtenir un flux de résultats et enfin l’écrire dans un fichier BED. Il s’agira de comparer ces positions à celles déterminées expérimentalement et de calculer les performances de prédictions. 2.7. Travaux réalisés et leurs résultats Pendant 1.5 mois, j’ai terminé une moitié du projet. En fait, j’ai implémenté l’algorithme de Brute-Force et l’algorithme de KMP en deux styles : fonctionnel et impérative. J’ai fait le benchmark pour évaluer leur performance. En plus, une table d’états est implémentée pour optimiser la performance de l’algorithme naïf. Le traitement du format FASTA est aussi réalisé. J’ai fait la lecture du fichier FASTA et stocké les données morceau par morceau. Enfin, j’ai travaillé sur la représentation les flux de données. J’ai réussi avec le type «générateur». Maintenant, je suis en train de travailler sur la deuxième idée. 2.7.1. Benchmark d’algorithme naïf et de KMP Dans le premier temps, le motif cherché et la chaine de caractères sont générés à partir des caractères alphabétiques. Dans le résultat obtenu, l’algorithme naïf du style fonctionnel performe mieux que celui du style impératif. Ce fait se produit aussi dans le cas de l’algorithme de KMP. En comparant les deux algorithmes, le temps d’exécution de l’algorithme naïf est plus court que celui de l’autre algorithme. Ce résultat est donc raisonnable. En fait, en profitant des caractères analogues dans le motif,
  • 20. 15 l’algorithme fait diminuer le nombre total d’itérations. Donc, sa complexité en moyen est baissée. Cependant, dans notre benchmark, les caractères du motif sont générés parmi vingtaine caractères alphabétiques. Donc, peu répétitions des caractères se produit. L’algorithme de KMP ne peut pas ainsi réduire les itérations mais couter plus de temps pour créer son tableau. Dans ce test, cet algorithme est en pire cas. Avec un motif biologique, l’alphabet est beaucoup réduit et l’algorithme peut mieux tirer parti de la table. J’ai fait un changement pour que le motif et la chaine de caractères soient générés à partir de deux caractères. L’algorithme de KMP est alors plus efficace que l’autre. 2.7.2. Construction la table d’états On va initialiser une table de booléens de taille n + 1, où n est la taille du motif. Tous les éléments sauf le premier de la table sont faux. Cela est la table d’états quand on ne reçoit pas encore de caractère. Les tables suivantes sont construites : procédure contruire_table (donnée-résultat tab : tableau[0…n+1] de booléens, donnée motif : chaine de caractères, donnée c: caractère) {précondition : la précédente table initialisée, le motif de recherche et un caractère données} {post condition : la table d’états quand on reçoit le i-ième caractère} variables j : entier n : entier début n <- taille_motif (motif) pour j de n à 0 faire tab.(j) <- tab.(j - 1) ^ (comparaison (c, motif.(j))) fin pour fin Certainement, la procédure ci-dessus s’écrit fonctionnellement. Ainsi, cette fonction marche bien avec l’algorithme naïf. Malheureusement, cette fonction est parfois inutile. Le problème se produit quand on veut imprimer exactement la chaine de caractères où on trouve le motif. En fait, dans la biologie, quelques fois, peu différence dans les deux chaines de caractères est acceptable. Dans ce cas, on veut savoir
  • 21. 16 explicitement la sous-chaine qui conduit à la correspondance. Donc, une table contient seulement l’état précédent ne suffit pas. 2.7.3. Lecture de fichier FASTA Ici, je vais aborder la lecture du fichier FASTA. Le fichier va être lu morceau par morceau. À chaque lecture, chaque caractère doit être considéré si cela appartient d’une description ou d’une séquence. En effet, la plupart des «>» représente une description. En plus, j’ai un drapeau pour signaler l’état (description ou séquence) du flux de données à chaque moment du parcours. Je stocke aussi une position qui marque le commencement de la description ou de la séquence. Si l’état du flux va être changé, c’est-à-dire une description (ou une séquence) est terminé, le précédent caractère ainsi appartient d’une séquence (ou une description), je sauvegarde tout ce que j’ai déjà parcouru à partir de la position de commence, puis je réinitialise la valeur de la position marquée. Si la chaine de caractères est terminée mais aucun signal de changement l’état n’est trouvé, je sauvegarde la chaine de caractères et change la position marquée sans faire changer l’état du flux. En effet, on a accepté que la séquence de DNA peut être sauvegarde en plusieurs morceaux, parce qu’elle est très longue. Par contre, le commentaire découpé peut se concaténer. Parce qu’il est court, le cout de concaténation est bas. 2.8. Travaux prévus Dans la deuxième partie du stage, les tâches suivantes seront faites : - la deuxième représentation du flux ; - l’écriture dans le fichier BED ; - l’implémentation de l’algorithme de recherche sur le motif de PWM ; - l’intégration des fonctions produites ; - la documentation.
  • 22. 17 3. Retour sur expérience Dans cette partie, je vais indiquer les expériences que j’ai acquises pendant le stage. J’aborde en premier mes connaissances sur la programmation fonctionnelle et sur le langage OCaml. Je vais parler ensuite les autres compétences techniques que j’ai renforcées. Cette partie sera terminée par mes expériences professionnelles. 3.1. Apprentissage du langage OCaml et de la programmation fonctionnelle Après l’apprentissage, je peux répondre aux questions suivantes : Qu’est-ce que la programmation fonctionnelle ? Pourquoi utilise-t-on ce paradigme ? Quelles sont ses propriétés ? Pourquoi utilise-t-on l’OCaml ? Quels sont les avantages du langage ? 3.1.1. Programmation fonctionnelle La programmation fonctionnelle est un paradigme moderne de programmation dont l’unité de base est la fonction. C’est-à-dire, ce paradigme de programmation se base sur l’application de fonctions mathématiques. En fait, l’utilisateur donne l’expression ou la déclaration. Ainsi, le programme en profite pour évaluer les fonctions pour retourner le résultat correspondant. La programmation fonctionnelle ne permet pas la modification des variables ni le changement d’états. C’est la différence principale entre ce paradigme avec les autres, qui utilisent souvent les phrases impératives pour guider le programme en un ou plusieurs étapes obtenir le résultat. La programmation fonctionnelle possède plusieurs atouts. D’abord, la gestion d’espace mémoire se lance automatiquement. L’utilisateur donc n’a pas soucis du tas, de la pile et d’allocation de l’espace mémoire. Ensuite, l’utilisateur peut facilement prendre une fonction comme argument sans l’utilisation d’un pointeur de fonction (comme d’autres paradigmes). Cet avantage sera représenté plus clair dans les parties suivantes. La programmation fonctionnelle est aussi très utile pour les callbacks (utilisés dans les IHMs pour les boucles d'évènements) et très excellente pour exprimer des algorithmes génériques. Beaucoup de fonctions peuvent être appliquées avec n'importe quel type de données. Par exemple, la fonction List.map peut s’utiliser avec les listes des entiers, des caractères, des listes, etc.
  • 23. 18 Les contraintes du sujet sont la dernière raison pour laquelle on choisit la programmation fonctionnelle. En effet, la plupart des fonctions de ce paradigme n’a aucun effet de bord. Donc, on peut utiliser les fonctions sans soucis d’une sorte d'état caché gardée. 3.1.2. Langage OCaml OCaml est un langage de programmation fonctionnelle. Dans l’OCaml, les types de données sont synthétisés et gérés par l’ordinateur. Le logiciel OCaml est offert et utilisable sur presque tous plate-forme Unix et Windows. Outre les avantages d’un langage de programmation fonctionnelle, OCaml garde aussi ses propres atouts. D’abord, il autorise les effets de bord au travers des références et des tableaux. OCaml est donc plus pratique parce que les fonctions avec une sorte d’état caché gardée sont parfois utile. Un autre avantage de l’OCaml est son évaluation stricte. Cette caractéristique rend ainsi un résultat plus raisonnable. Par contre, si l’utilisateur veut faire quelques choses bizarres, l’OCaml ne l’empêche pas. Enfin et surtout, l’OCaml attire une base d’utilisateurs importante grâce aux ses services hors de la programmation fonctionnelle. En effet, l’OCaml est très apprécié grâce au service de la programmation orientée d’objet et du système de type. 3.2. Compétences techniques acquises en OCaml Dans la programmation fonctionnelle, particulièrement dans le langage OCaml, il y a des nouvelles notions que nous utilisons rarement dans d’autres paradigmes et langages. En fait, quelques notions sont déjà connues. Par contre, je vais les montrer aussi dans cette partie, parce qu’elles sont les plus importantes et très utiles. 3.2.1. Variable non modifiable Dans la programmation fonctionnelle, surtout l’OCaml, on ne peut pas modifier les variables. Elles sont donc gérées automatiquement. Cela nous semble un massif avantage. Par contre, pour profiter cet atout, nous devons donner une valeur à chaque fois une variable initialisée. Cette propriété est donc contraire avec notre habitude : initialiser une variable, puis affecter sa valeur.
  • 24. 19 Par exemple, une variable peut être initialisée : # let x = 5;; val x : int = 5 ou # let y = String.length “ABCD”;; val y: int = 4 Par contre, on ne peut pas écrire comme suivant : # let z ;; 3.2.2. Typage fort L’OCaml est un langage de typage fort. C’est-à-dire, les variables utilisées a totalement et seulement un type. Donc, les conversions implicites de types sont formellement interdites. On a un exemple : # let double x = x * 2 ;; val double : int -> int Dans cet exemple, on va multiplier un entier avec 2. Dans d’autres langages, si l’on veut la multiplication d’un nombre réel, on peut également utiliser cette fonction. Par contre, dans l’OCaml, cela n’est permis pas. On doit ainsi écrire une autre fonction : # let double_reel x = x *. 2. ;; val double_reel : float -> float On va appliquer la fonction ci-dessus : # double_reel 1. ;; :- float = 2. Si l’on écrit : # double_reel 2 ;; cela va rend une erreur.
  • 25. 20 3.2.3. Inférence de type Grâce au typage fort, on a un mécanisme de trouver le type de la fonction. Ainsi, le compilateur trouve simplement le type le plus général qui puisse prendre l'expression. Par conséquent, une fonction ne s’exécute pas une fois que son type soit inapproprié. Supposons l’exemple suivant : # let isvowel c = match c with ‘a’ -> true | ‘e’ -> true | ‘i’ -> true | ‘o’ -> true | ‘u’ -> true | _ -> false ;; val isvowel : char -> bool Cette fonction va prendre un caractère et retourner vrai si c’est une voyelle. Si l’on écrit : # isvowel ‘’a’’;; cela va rendre une erreur, parce que ‘’a’’ n’est pas du type char. Pour évaluer le caractère a, on doit le mettre entre les apostrophes. 3.2.4. Fonction d’ordre supérieur Dans programmation fonctionnelle, on se permet d’utiliser une fonction en tant qu’argument dans une autre fonction. La fonction qui prend une autre fonction comme son argument est la fonction d’ordre supérieur. On a un exemple suivant : # let double ls = List.map ( fun x -> x * 2 ) ls ;; val double : int list -> (int -> int) -> int list # double [1 ; 2 ; 3] ;; :- int list = [2 ; 4 ; 6] Dans cet exemple, la fonction (fun x -> x * 2) prend un argument x et retourne x * 2. J’ai appliqué cette fonction pour tous les éléments d’une liste d’entiers
  • 26. 21 par l’appel de la fonction map. C’est-à-dire, la fonction map prend la fonction (fun x -> x * 2) comme argument. Donc, la fonction map s’appele une fonction d'ordre supérieur. 3.2.5. Fonction récursive et fonction récursive terminale Une fonction est récursive si cette fonction utilise lui-même pour obtenir le résultat. La fonction suivant est récursive : # let rec length l = match l with [] -> 0 |_ ::t -> 1 + length t Une fonction récursive est dit terminale si le résultat du dernier appel de cette fonction rend exactement la valeur de retour du programme. On a aussi la version récursive terminale de la fonction ci-dessus comme suivant : # let rec length_inner l n = match l with [] -> n | _ ::t -> length_inner t (n + 1) # let rec length l = length_inner l 0 Dans programmation fonctionnelle, on utilise souvent les fonctions récursives. La complexité de la fonction récursive est meilleure que celle non- récursive. Cependant, grâce à la comparaison ci-dessus, les fonctions récursives terminales sont les plus appréciées dans OCaml. 3.2.6. Programmation impérative dans OCaml Comme on le sait, la programmation fonctionnelle ne permet pas de changer les variables. Par contre, les utilisateurs d’OCaml peuvent également écrire impérativement leurs programmes. L’OCaml fournit une construction s’appelé référence qui est une boite dans laquelle on peut stocker une valeur. Nous avons un exemple suivant :
  • 27. 22 # let x = ref 0 ;; val x : int ref = {contents = 0} Pour extraire le contenu courant d’une référence, on utilise l’opérateur «!», comme l’exemple suivant : # let p = !x ;; val p : int = 0 Pour mettre à jour le contenu d’un référence, on utilise l’opérateur «:=». # x := 50 ;; - : unit = () Une nouvelle valeur est mise dans la variable x, puis rien n’est retourné. Le type unit est ainsi pour but de représenter «rien». 3.2.7. Type optionnel Dans réalité, nous avons d’un type qui représente soit rien, soit quelque chose du type «α». Donc, l’OCaml nous permet de définir ce type comme suivant : # type ‘a option = None | Some of ‘a ;; type ‘a option = None | Some of ‘a Un exemple de l’utilisation de ce type: # let nothing = None ;; val nothing : ‘a option = None # let number = Some 50;; val number: int option = Some 50 3.2.8. Application partielle On a une fonction comme suivant : # let plus x y = x + y ;; val plus : int –> int -> int Du côté informatique, cette fonction est une fonction qui prend deux arguments x et y et retourne x + y.
  • 28. 23 Du côté mathématique, cette fonction est une fonction qui prend un entier x et renvoie une fonction qui si l’on lui donne un entier y, retournera entier x + y. Cette fonction peut également être écrite comme suivant : # let plus x = fun y -> x + y ;; val plus : int -> (int -> int) La partie (int -> int) peut ainsi décrite comme une fonction qui prend un entier et retourne un entier. Donc, si l’on écrit : # let f = plus 1 ;; val f : int -> int on a surement une fonction qui prend un entier et retourne son successeur. La fonction f est appelé une application partielle de la fonction plus, parce f a appliqué la fonction plus avec 1 et retourne une fonction du type int -> int. 3.3. Autres compétences techniques Outre la technique de programmation fonctionnelle, j’améliore ma compétence d’analyse de la complexité. En effet, beaucoup d’éléments contribuent à la performance d’un programme : algorithme, codage, système d’exploitation, etc. Donc, pour évaluer un programme, il me faut remarquer plusieurs aspects. Je me renforce aussi en la communication avec le fichier. En effet, cette mission me donne une grande occasion pour faire la lecture et l’écriture dans les fichiers. Ce stage est aussi un cas concret dont j’utilise la connaissance de l’automate et des mathématiques. En fait, les fonctions mathématiques sont continument utilisées pendant ce stage. Beaucoup de problèmes peuvent donc être résolus de manière fonctionnelle. J’ai aussi l’occasion d’utiliser le GIT, un gestionnaire des versions du code. Outre la connaissance de base, mon tuteur m’aide beaucoup sur le mécanisme de diviser pour régner et me donne une vue générale du problème. Beaucoup de problèmes de grande taille peuvent se résoudre en plusieurs sous-problèmes analogues. Cette technique est très importante pour la construction des grands programmes. 3.4. Côté professionnel J’ai l’environnement pour pratiquer mon français. En plus, j’ai beaucoup de chances de discussion et d’explication avec mon tuteur sur le thème scientifique. Le niveau de mon sens critique est aussi meilleur.
  • 29. 24 Conclusion Après une moitié du stage, quelques conclusions peuvent être tirées. D’abord, le stage au sein de PRABI me permet de découvrir cette organisation et son fonctionnement. En effet, les centres de recherche comme PRABI sont motivés par le développement de la science. En outre, je comprends bien la connexion étroite entre les composants de recherche. En effet, un projet de recherche doit être travaillé par plusieurs équipes. C’est la raison pour laquelle les grands changements se produisent. Ensuite, travailler dans un département de bioinformatique m’aide à trouver l’harmonie entre deux domaines scientifiques : biologie et informatique. En fait, l’informatique valorise des activités de recherche biologique. En reverse, la biologie crée l’environnement pratique pour que les innovations informatiques soient utilisées. Après, pendant 1.5 mois, j’ai appris un nouveau paradigme de programmation et un langage moderne. En outre, j’ai travaillé partiellement sur le sujet de recherche d’un motif en espace mémoire constant dans un génome. Mon travail contribue ainsi une petite partie à la recherche scientifique, surtout la recherche en bioinformatique. Ce stage m’aide aussi à renforcer mes savoirs théoriques. En effet, j’améliore ma compétence d’analyse de la complexité. En plus, je maitrise de la technique de lecture et d’écriture des fichiers. En outre, j’ai un nouveau point de vue sur l’usage de l’automate. Ainsi, je connais bien le rôle de la connaissance théorique et prends conscience des conditions réelles d'exercice. Dernièrement, en travaillant hors de l’environnement informatique, je trouve aussi l’importance de la technologie d’information dans la vie, particulièrement dans le domaine de recherche. Donc, l’informatique est vraiment une évolution du monde. Comme tous les jeunes, j’aimerais bien contribuer et m’attacher à ce domaine scientifique.
  • 30. 25 Bibliographie 1. Livre WHITINGTON John. OCaml from the very beginning. Royaume-Uni. Coherent Press. 2013. 194 pages. 2. Sites internet consultés https://fr.wikipedia.org/wiki/Programmation_fonctionnelle https://ocaml.org/learn/tutorials/functional_programming.fr.html http://caml.inria.fr/about/history.en.html http://caml.inria.fr/resources/doc/faq/general.en.html http://caml.inria.fr/pub/docs/manual-ocaml/libref/index.html http://pbil.univ-lyon1.fr/members/veber/ocaml/fold.html https://fr.wikipedia.org/wiki/FASTA_(format_de_fichier) https://fr.wikipedia.org/wiki/BED_(format_de_fichier) https://en.wikipedia.org/wiki/Position_weight_matrix https://blogs.janestreet.com/core_bench-micro-benchmarking-for-ocaml/ https://github.com/c-cube/gen