TIPE2021/Codes.ml

166 lines
5.9 KiB
OCaml

(* Implémentation des codes linéaires *
* un code linéaire est une application linéaire f de S = {0,1}^k dans D = {0,1}^n injective
* le terme code désigne aussi l'espace C = Im(f)
* k représente la dimension de l'espace source, on l'appelle dimension du code
* n représente la dimension de l'espace des codes, on l'appelle longueur du code
* on modèlise un code linéaire par sa matrice génératrice G, celle-ci possède n lignes et k colonnes, elle est à coefficients dans {0,1}
* tout calcul matriciel se fait dans le corps F_2
* nous nous intéressons dans un premier temps à des codes systématiques càd recopiants le mot d'entré puis rajoutants des redondances
* pour aider le décodage on introduit le concept de matrice de contrôle H dont on n'a pas l'unicité, il s'agit d'une matrice comportant n-k lignes et n colonnes et dont le noyau correspond à l'image du code
* la distance minimale d_f d'un code correspond à la plus petite distance séparant deux mots distincts du code
* on définit à partir de celle-ci la capacité de détection e_d du code ainsi que celle de correction e_c
* un code est parfait si pour tout mot de code M de D, il existe un unique Y appartenant à C minimisant la distance de M à Y
* on souhaite transmettre un mot source X, pour ce faire on l'encode en Y = f(X) puis on envoit Y, après transmission (et donc des apparitions d'erreurs) est reçu Z
* notre but est de construire un algorithme capable de déterminer le mot de code Y' le plus proche de Z et donc de décoder Z en X' = f<-1>(Y')
* on pose E = Z + Y le mot d'erreur associé à Z
* pour M un mot de code, on appelle syndrome de M le mot HM, on note en particulier S le syndrome de Z
* en remarquant que E est dans la classe lattérale de Z (càd qu'il a le même syndrome que Z), la recherche de Y' se ramène à celle du mot de plus petit poids (au sens de Hamming) dans la classe lattérale de Z
*NOTE SUR LA PROGRAMMATION*
* on représente un vecteur dans F_2 par un entier dont la décomposition binaire correspond aux composantes du vecteur
* on représente une matrice par un liste d'entier, il s'agit de la liste de ses colonnes (qui sont donc des vecteurs)
*)
(* La bonne structure *)
type code_lineaire = {k : int; n : int; g : int list; h : int list};;
(* Effectue le produit matriciel 'matrice' . 'vecteur' *)
let produit matrice vecteur =
let rec auxiliaire resultat_partiel masque = function
| [] -> resultat_partiel
| colonne :: reste ->
let resultat =
if masque mod 2 = 1
then (lxor) colonne resultat_partiel
else resultat_partiel
in auxiliaire resultat (masque / 2) reste
in auxiliaire 0 vecteur matrice
;;
produit [14; 5; 23] 6;;
(* Calcul Y = GX *)
let encoder code x = produit code.g x;;
(* Ne calcul papy *)
let deux_puissance = (lsl) 1;;
deux_puissance 11;;
(* Construit la matrice identité de taille d.d *)
let identite d =
let rec sub acc = function
| p when p >= 0 -> sub ((deux_puissance p) :: acc) (p-1)
| _ -> acc
in sub [] (d-1)
;;
identite 3;;
(* Le nom de cette fonction n'est pas assez explicite *)
let construire_code_lineaire_systematique k n redondance =
let g =
let decalage = deux_puissance k in
let rec iteredon ajout = function
| [] -> []
| tete :: queue ->
let c = tete * decalage + ajout in
c :: (iteredon (ajout * 2) queue)
in iteredon 1 redondance
and h = redondance @ (identite (n-k)) in
{k = k; n = n; g = g; h = h}
;;
(* Un classique : code de Hamming (4, 7)
1 0 0 0
0 1 0 0
0 0 1 0
0 0 0 1
1 1 1 0 1 0 0
1 1 0 1 0 1 0
1 0 1 1 0 0 1
-> les 4 premières colonnes : G
-> les 3 dernières lignes : H
*)
let code_hamming =
construire_code_lineaire_systematique 4 7 [7; 3; 5; 6]
;;
encoder code_hamming 6;;
(* Change l'état du 'i'-eme bit *)
let changer_bit i = (lxor) (deux_puissance i);;
changer_bit 2 6;;
(* Vérifie que 'y' respecte toutes les contraintes de 'cs' *)
let respecter y cs =
let ny = (lnot) y in
List.fold_right (fun c b -> b && (land) c ny > 0) cs true
;;
respecter 7 [3];;
(* Etant donné touts les vecteurs de poids p dans un espace de dimension d, retourne touts ceux de poids p+1 dans ce même espace *)
let suivants d vecteurs =
let contraintes = ref [] in
let resultats = ref [] in
let rec iterer = function
| [] -> ()
| x :: r ->
let donne_un_resultat = ref false in
for i=0 to d-1 do
let y = changer_bit i x in
if x < y && (respecter y !contraintes)
then begin
resultats := y :: !resultats;
donne_un_resultat := true;
end;
done;
if !donne_un_resultat
then contraintes := x :: !contraintes;
iterer r;
in iterer vecteurs; !resultats
;;
suivants 3 [7];;
(* Renvoit le plus petit mot (au sens de Hamming) dans F_2^d vérifiant 'propriete' et de poids inférieur à poids_max. Renvoie le couple (-1, 0) si aucun mot n'a été trouvé *)
let plus_petit_verifiant propriete poids_max d =
let rec chercher p vecteurs =
match List.find_opt propriete vecteurs with
| Some v -> (p, v)
| None ->
if p < poids_max
then chercher (p+1) (suivants d vecteurs)
else (-1, 0)
in chercher 0 [0]
;;
let appartenir code v = produit code.h v = 0;;
(* Calcul la distance minimale d'un code *)
let distance_minimale code =
let n = code.n in
let propriete = fun v -> (0 < v) && (appartenir code v) in
let (p, _) = plus_petit_verifiant propriete n n in p
;;
distance_minimale code_hamming;;
(* Calcul de façons à ouf l'antécédent de 'y' pour le code 'code' *)
let antecedent code y =
let mot_max = (deux_puissance code.k) - 1 in
let rec iterer = function
| x when x = mot_max -> failwith "y n'appartient pas au code"
| x ->
if (encoder code x) = y
then x else iterer (x+1)
in iterer 0
;;
(* Applique notre algorithme préféré *)
let decoder code z =
let d_min = distance_minimale code in
let e_c = (d_min - 1) / 2 and n = code.n in
let propriete = fun v -> appartenir code ((lxor) z v) in
match plus_petit_verifiant propriete e_c n with
| (-1, 0) -> failwith "on ne peut décoder"
| (_, e) -> antecedent code ((lxor) z e)
;;
decoder code_hamming 20;;