#import "@preview/polylux:0.4.0": * #import "@preview/cetz:0.5.0" #import "@preview/cetz-plot:0.1.3": plot #import "@preview/cetz:0.5.0": canvas #set math.mat(delim: "[") // Infos #let auteur = "Anthony PERRONI" #let numero = "49871" #let titre = "Codes LDPC" #let annee = "2025 - 2026" // Template #let myslide(partie, contenu) = slide[ // Header #block(width: 100%, fill: black, inset: (top: 0.6cm, bottom: 0.6cm, left: 1.5cm, right: 1.5cm))[ #set text(fill: white, size: 28pt, weight: "bold") #partie ] // Contenu #pad(x: 1.5cm, top: 0.2cm)[ #set text(size: 20pt) #contenu ] // Footer #v(1fr) #context { let cur = counter("logical-slide").get().first() let fins = query() let tot = if fins.len() > 0 { counter("logical-slide").at(fins.first().location()).first() } else { counter("logical-slide").final().first() } if cur > 1 { block(width: 100%, fill: black, inset: (top: 0.2cm, bottom: 0.2cm, left: 1.5cm, right: 1.5cm))[ #set text(fill: white, size: 12pt, weight: "bold") #grid( columns: (1.5fr, 2fr, 1fr, auto), align: (left, center, center, right), [#auteur n°#numero], [#titre], [#annee], if cur <= tot [#(cur - 1) / #(tot - 1)] else [Annexe], ) ] } } ] // Plan #let design_plan(items) = { let r = 6pt let epaisseur = 2pt let hauteur_ligne = 2.5em let espace_vertical = 20pt pad(left: 1cm, top: 1cm)[ #block[ #place(dx: r, dy: hauteur_ligne / 2)[ #line( length: (items.len() - 1) * (hauteur_ligne + espace_vertical), angle: 90deg, stroke: epaisseur + black, ) ] #grid( columns: (2 * r, auto), column-gutter: 20pt, row-gutter: espace_vertical, ..items .map(item => ( box(height: hauteur_ligne, align(center + horizon)[ #circle(radius: r, fill: white, stroke: epaisseur + black) ]), box(height: hauteur_ligne, align(left + horizon)[ #text(weight: "bold", size: 1.1em)[#item] ]), )) .flatten() ) ] ] } // Graphe de Tanner en filigrane #let graphe_tanner_fond(taille, spread) = { cetz.canvas(length: taille, { import cetz.draw: * let n_var = 18 let n_chk = 9 let var_y = 11 let chk_y = -8 // Calcul automatique pour que le graphe soit centré sur X=0 let dist_v = (n_var - 1) * spread let v_start = -dist_v / 2 let vpos = range(n_var).map(i => (v_start + i * spread, var_y)) let spread_c = dist_v / (n_chk - 1) let c_start = -dist_v / 2 let cpos = range(n_chk).map(i => (c_start + i * spread_c, chk_y)) let edges = ( (0, 0), (0, 4), (0, 8), (1, 1), (1, 5), (1, 0), (2, 2), (2, 6), (2, 1), (3, 3), (3, 7), (3, 2), (4, 4), (4, 8), (4, 3), (5, 5), (5, 0), (5, 4), (6, 6), (6, 1), (6, 5), (7, 7), (7, 2), (7, 6), (8, 8), (8, 3), (8, 7), (9, 0), (9, 3), (9, 6), (10, 1), (10, 4), (10, 7), (11, 2), (11, 5), (11, 8), (12, 0), (12, 5), (12, 7), (13, 1), (13, 3), (13, 8), (14, 2), (14, 4), (14, 6), (15, 0), (15, 4), (15, 7), (16, 1), (16, 5), (16, 8), (17, 2), (17, 3), (17, 6), ) for e in edges { let vp = vpos.at(e.at(0)) let cp = cpos.at(e.at(1)) line((vp.at(0), vp.at(1)), (cp.at(0), cp.at(1)), stroke: 0.45pt + rgb("#e0e0e0")) } for p in vpos { circle((p.at(0), p.at(1)), radius: 0.25, fill: white, stroke: 0.8pt + rgb("#cccccc")) } for p in cpos { let (x, y) = p rect((x - 0.25, y - 0.25), (x + 0.25, y + 0.25), fill: white, stroke: 0.8pt + rgb("#cccccc")) } }) } // Schema Shannon // #let canal_shannon_intro() = { // cetz.canvas({ // import cetz.draw: * // // // Styles // let s = (stroke: 2pt + black) // let s_fleche = (stroke: 2pt + black) // let pointe_pleine = (end: "stealth", fill: black) // // let espacement = 5.5 // // // Blocs // content((0, 0), [Source], name: "src", frame: "rect", ..s, padding: .3) // content((espacement, 0), [Emetteur], name: "em", frame: "rect", ..s, padding: .3) // content((espacement * 2, 0), [Canal], name: "chan", frame: "rect", ..s, padding: .3) // content((espacement * 3, 0), [Récepteur], name: "rec", frame: "rect", ..s, padding: .3) // content((espacement * 4, 0), [Destinataire], name: "dest", frame: "rect", ..s, padding: .3) // // // Bruit // content((rel: (0, 1.8), to: "chan"), [Bruit], name: "bruit", frame: "rect", ..s, padding: .3) // // // Codage / Décodage // content((rel: (0, 1), to: "em"), [*Codage*]) // content((rel: (0, 1), to: "rec"), [*Décodage*]) // // // Flèches (On utilise s_fleche et pointe_pleine) // line("src.east", "em.west", mark: pointe_pleine, ..s_fleche) // line("em.east", "chan.west", mark: pointe_pleine, ..s_fleche) // line("chan.east", "rec.west", mark: pointe_pleine, ..s_fleche) // line("rec.east", "dest.west", mark: pointe_pleine, ..s_fleche) // line("bruit.south", "chan.north", mark: pointe_pleine, ..s_fleche) // // // Annotation // let note-style = (size: 0.75em, style: "italic") // content((espacement * 0.5, -0.5), text(..note-style)[Message]) // content((espacement * 1.5, -0.5), text(..note-style)[Signal]) // content((espacement * 2.5, -0.5), text(..note-style)[Signal]) // content((espacement * 3.5, -0.5), text(..note-style)[Message]) // }) // } #let canal_shannon_intro() = { // Couleurs let col-u = blue // Signal propre let col-p = orange // Signal bruité let C_MAIN = black let C_BG = rgb("#f8f9fa") // Le gris très clair demandé let C_LABEL = black cetz.canvas(length: 1cm, { import cetz.draw: * let x_left = 4.0 let x_right = 16.0 let x_noise = 22.5 let y_top = 14.5 let y_mid = 9.0 let y_bot = 3.5 // Style des lignes avec flèches let s_ligne = (stroke: 1.5pt + C_MAIN, mark: (end: "stealth", fill: C_MAIN, size: 0.25)) let s_bruit_fleche = ( stroke: (paint: black, thickness: 1.5pt, dash: "dashed"), mark: (end: "stealth", fill: black, size: 0.25), ) // Fonction de bloc avec le nouveau fond let bloc(pos, txt, stxt, n) = { content( pos, [#set align(center); #text(C_MAIN, weight: "bold", size: 1.2em)[#txt] \ #text( C_MAIN, size: 0.85em, style: "italic", )[#stxt]], frame: "rect", fill: C_BG, stroke: C_MAIN + 1.5pt, padding: 0.7, radius: 0.3, name: n, ) } // --- ONDES (Bruit dosé : "tremblement" au lieu de "chaos") --- let wave_vert(x, y_start, y_end, is_noisy) = { let n_points = 200 let color = if is_noisy { col-p } else { col-u } line( ..range(n_points + 1).map(i => { let t = i / n_points let y = y_start + t * (y_end - y_start) // Enveloppe pour raccord propre let env = if t < 0.05 { t / 0.05 } else if t > 0.95 { (1 - t) / 0.05 } else { 1.0 } let signal = 0.35 * calc.sin(t * 6 * calc.pi) // Bruit dosé : on a baissé les fréquences et l'amplitude let bruit = if is_noisy { (0.08 * calc.sin(t * 45 * calc.pi) + 0.05 * calc.cos(t * 97 * calc.pi)) } else { 0 } (x + (signal + bruit) * env, y) }), stroke: color + 1.6pt, ) } wave_vert(x_right, y_top - 1.5, y_mid + 1, false) // Bleu wave_vert(x_right, y_mid - 1, y_bot + 1.5, true) // Orange // --- BLOCS --- bloc((x_left, y_top), "Source", "Information", "src") bloc((x_right, y_top), "Émetteur", "Codage", "em") bloc((x_right, y_mid), "Canal", "", "canal") bloc((x_noise, y_mid), "Bruit", "", "bruit") bloc((x_right, y_bot), "Récepteur", "Décodage", "rec") bloc((x_left, y_bot), "Destinataire", "Information", "dest") // --- CONNEXIONS --- line("src.east", "em.west", ..s_ligne) line("bruit.west", "canal.east", ..s_bruit_fleche) line("rec.west", "dest.east", ..s_ligne) // --- ANNOTATIONS --- let lab(pos, body, color: C_LABEL, anchor: "center") = content( pos, text(size: 0.95em, fill: color, style: "italic", weight: "medium")[#body], anchor: anchor, ) lab(((x_left + x_right) / 2 - 0.3, y_top + 0.7), "Message") lab((x_right + 1.2, (y_top + y_mid) / 2), "Signal", color: col-u, anchor: "west") lab((x_right + 1.2, (y_mid + y_bot) / 2), "Signal + Bruit", color: col-p, anchor: "west") lab(((x_left + x_right) / 2 + 0.3, y_bot - 0.7), "Message reçu") }) } #let plongement_schema() = { cetz.canvas(length: 1.5cm, { import cetz.draw: * // Styles let style-pointille = (stroke: (paint: black, thickness: 1.2pt, dash: "dashed")) let style-point = (fill: black, stroke: none) let rayon-point = 0.08 let pointe-fleche = (end: "stealth", fill: black) let m = ( p00: (-4, 0), p01: (-2, 0), p11: (-2, -2), p10: (-4, -2), ) // Arêtes pointillés line(m.p00, m.p01, m.p11, m.p10, close: true, ..style-pointille) // Points sur les sommets circle(m.p00, radius: rayon-point, ..style-point) circle(m.p01, radius: rayon-point, ..style-point) circle(m.p11, radius: rayon-point, ..style-point) circle(m.p10, radius: rayon-point, ..style-point) // Étiquettes content(m.p00, [00], anchor: "south-east", padding: .2) content(m.p01, [01], anchor: "south-west", padding: .2) content(m.p11, [11], anchor: "north-west", padding: .2) content(m.p10, [10], anchor: "north-east", padding: .2) content((-3, -2.6), [$FF_2^2$]) line((-1.2, -1), (1.2, -1), mark: pointe-fleche, stroke: 1.5pt + black) content((0, -0.6), [*Plongement*]) // Sommets face arrière let cb = (p000: (2.5, 0), p001: (4.5, 0), p011: (4.5, -2), p010: (2.5, -2)) // Sommets face avant (décalés) let cf = (p100: (3.5, 1), p101: (5.5, 1), p111: (5.5, -1), p110: (3.5, -1)) // Arêtes pointillés du cube line(cb.p000, cb.p001, cb.p011, cb.p010, close: true, ..style-pointille) // Face arrière line(cf.p100, cf.p101, cf.p111, cf.p110, close: true, ..style-pointille) // Face avant line(cb.p000, cf.p100, ..style-pointille) // Liaisons line(cb.p001, cf.p101, ..style-pointille) line(cb.p011, cf.p111, ..style-pointille) line(cb.p010, cf.p110, ..style-pointille) // Points sur les sommets du cube circle(cb.p000, radius: rayon-point, ..style-point) circle(cb.p001, radius: rayon-point, ..style-point) circle(cb.p011, radius: rayon-point, ..style-point) circle(cb.p010, radius: rayon-point, ..style-point) circle(cf.p100, radius: rayon-point, ..style-point) circle(cf.p101, radius: rayon-point, ..style-point) circle(cf.p111, radius: rayon-point, ..style-point) circle(cf.p110, radius: rayon-point, ..style-point) // Étiquettes du cube content(cb.p000, [000], anchor: "south-east", padding: .2) // content(cb.p001, [001], anchor: "south-west", padding: .2) content((rel: (-0.85, 0.1), to: cb.p001), [001], anchor: "south-west", padding: .2) content(cb.p011, [011], anchor: "north-west", padding: .2) content(cb.p010, [010], anchor: "north-east", padding: .2) content(cf.p100, [100], anchor: "south-east", padding: .2) content(cf.p101, [101], anchor: "south-west", padding: .2) content(cf.p111, [111], anchor: "north-west", padding: .2) // content(cf.p110, [110], anchor: "north-east", padding: .2) content((rel: (0.85, -0.1), to: cf.p110), [110], anchor: "north-east", padding: .2) content((4, -2.6), [$FF_2^3$]) }) } #let definition( titre: "Définition", accent: black, titre_taille: 1.13em, corps_taille: 1.2em, contenu, ) = { block( width: 100%, fill: rgb("#f8f9fa"), stroke: 0.5pt + gray.lighten(60%), radius: 8pt, clip: true, grid( columns: (5pt, 1fr), rows: auto, fill: (col, row) => if col == 0 { accent }, [], block( width: 100%, inset: (x: 16pt, y: 15pt), { { set text(fill: accent, weight: 800, size: titre_taille, tracking: 0.5pt) show math.equation: set text(weight: 700, size: 1.05em) titre } v(18pt, weak: true) { set text(size: corps_taille) set par(leading: 0.7em, justify: false) contenu } }, ), ), ) } #let limite_shannon_graphique() = { let col-shannon = red.darken(10%) let col-code-long = blue.darken(20%) let col-code-short = gray.lighten(10%) set text(size: 11pt) move(dx: -5pt, dy: 0pt)[ #cetz.canvas({ import cetz.draw: * plot.plot( size: (23, 15), // 1. On grossit les labels d'axes (15pt) x-label: pad(bottom: 30pt)[#move(dy: 80pt)[#text(size: 18pt)[$E_b/N_0$ (dB)]]], y-label: move(dx: -20pt, dy: -10pt)[#text(size: 18pt)[Bit Error Rate (BER)]], x-min: 0, x-max: 10, y-min: -8.5, y-max: 0.2, x-tick-step: 1, y-tick-step: 2, // Axis x-format: x => move(dy: 14pt)[#text(size: 16pt)[#x]], y-format: y => move(dx: -5pt)[#text(size: 16pt)[$10^(#y)$]], x-grid: true, y-grid: true, // Légende positionnée en bas. legend: "north-east", legend-style: ( stroke: 0.5pt + gray, fill: white, padding: 0.2, offset: (-161.4pt, -45.1pt), ), { // 1. Limite de Shannon (Asymptote) plot.add( ((1.5, -8.5), (1.5, 0.2)), style: (stroke: (paint: col-shannon, thickness: 2pt, dash: "dashed")), label: [Limite de Shannon], ) // 2. Petit bloc linéaire (n ≈ 100) - Ultra-densifié (smooth) plot.add( ( (0.0, 0.00), (0.2, -0.01), (0.4, -0.03), (0.6, -0.05), (0.8, -0.07), (1.0, -0.10), (1.2, -0.13), (1.4, -0.17), (1.6, -0.21), (1.8, -0.25), (2.0, -0.30), (2.2, -0.36), (2.4, -0.43), (2.6, -0.51), (2.8, -0.60), (3.0, -0.70), (3.2, -0.80), (3.4, -0.91), (3.6, -1.03), (3.8, -1.16), (4.0, -1.30), (4.2, -1.44), (4.4, -1.59), (4.6, -1.75), (4.8, -1.92), (5.0, -2.10), (5.2, -2.28), (5.4, -2.47), (5.6, -2.67), (5.8, -2.88), (6.0, -3.10), (6.2, -3.32), (6.4, -3.55), (6.6, -3.79), (6.8, -4.04), (7.0, -4.30), (7.2, -4.56), (7.4, -4.83), (7.6, -5.11), (7.8, -5.40), (8.0, -5.70), (8.2, -6.00), (8.4, -6.31), (8.6, -6.63), (8.8, -6.96), (9.0, -7.30), (9.2, -7.62), (9.4, -7.95), (9.6, -8.29), (9.8, -8.64), (10.0, -9.00), ), style: (stroke: (paint: col-code-short, thickness: 1.5pt)), label: [Code court ($n approx 100$)], ) // 3. Grand bloc linéaire (n = 64 800) - Ultra-densifié (smooth & waterfall net) plot.add( ( (0.0, 0.0), (0.2, -0.005), (0.4, -0.01), (0.6, -0.02), (0.8, -0.035), (1.0, -0.05), (1.1, -0.07), (1.2, -0.10), (1.3, -0.14), (1.4, -0.20), (1.45, -0.28), (1.50, -0.40), (1.52, -0.45), (1.54, -0.52), (1.56, -0.60), (1.58, -0.69), (1.60, -0.80), (1.62, -0.92), (1.64, -1.05), (1.66, -1.19), (1.68, -1.34), (1.70, -1.50), (1.72, -1.69), (1.74, -1.89), (1.76, -2.11), (1.78, -2.35), (1.80, -2.60), (1.82, -2.88), (1.84, -3.18), (1.86, -3.50), (1.88, -3.84), (1.90, -4.20), (1.92, -4.52), (1.94, -4.86), (1.96, -5.22), (1.98, -5.60), (2.00, -6.00), (2.02, -6.32), (2.04, -6.64), (2.06, -6.94), (2.08, -7.23), (2.10, -7.50), (2.12, -7.66), (2.14, -7.81), (2.16, -7.95), (2.18, -8.08), (2.20, -8.20), (2.22, -8.32), (2.24, -8.44), (2.25, -8.50), ), style: (stroke: (paint: col-code-long, thickness: 3.5pt)), label: [Code long ($n = 64\,800$)], ) }, ) }) ] } #let decor_matrice_etoilee() = { // Ajuste length (ex: 0.6mm ou 0.7mm) selon ta diapo cetz.canvas(length: 0.7mm, { import cetz.draw: * // --- 1. Paramètres de la Matrice --- let nx = 430 let ny = 265 // --- 2. Paramètres de la Loupe --- let loupe_x = nx * 0.70 let loupe_y = -ny / 2.0 let R = 50.0 let zoom = 3.0 // --- 3. Paramètre d'Estompage (Gauche uniquement) --- let fade_left = 220.0 // Palette de couleurs let col_focus = rgb("#0284c7") let col_fade = rgb("#64748b") let col_grid = rgb("#e2e8f0") // --- Fonction pseudo-aléatoire --- let pseudo_rand(x, y) = { let v = x * 7919 + y * 104729 + (x * x) * 313 + (y * y) * 991 + (x * y) * 101 calc.rem(calc.abs(v), 100) } // --- ÉTAPE A : Dessin de la matrice (Fade à gauche uniquement) --- for x in range(nx) { for y in range(ny) { let hash = pseudo_rand(x, y) let is_active = hash < 12 if is_active { // L'intensité ne dépend plus que de la position X let intensity = calc.min(1.0, x / fade_left) if intensity > 0.05 { let radius = 0.20 + 0.15 * intensity let c = col_fade.lighten((1.0 - intensity) * 80%) circle((x, -y), radius: radius, fill: c, stroke: none) } } } } // --- ÉTAPE B : Dessin de la Loupe --- circle((loupe_x, loupe_y), radius: R, fill: white, stroke: none) // Grille zoomée let limit = 20 for k in range(-limit, limit + 1) { let offset = k * zoom if calc.abs(offset) < R { let half_chord = calc.sqrt(R * R - offset * offset) line( (loupe_x + offset, loupe_y + half_chord), (loupe_x + offset, loupe_y - half_chord), stroke: 0.5pt + col_grid, ) line( (loupe_x - half_chord, loupe_y + offset), (loupe_x + half_chord, loupe_y + offset), stroke: 0.5pt + col_grid, ) } } // Points zoomés let search_r = int(R / zoom) + 2 let lx_int = int(loupe_x) let ly_idx = int(-loupe_y) for x in range(lx_int - search_r, lx_int + search_r + 1) { for y in range(ly_idx - search_r, ly_idx + search_r + 1) { let hash = pseudo_rand(x, y) if hash < 12 { let dx = x - loupe_x let dy = -y - loupe_y let d_orig = calc.sqrt(dx * dx + dy * dy) let d_zoom = d_orig * zoom if d_zoom <= R - 1.2 { circle((loupe_x + dx * zoom, loupe_y + dy * zoom), radius: 1.1, fill: col_focus, stroke: none) } } } } // Bordures de la Loupe circle((loupe_x, loupe_y), radius: R, stroke: 1.8pt + col_focus.lighten(20%)) circle((loupe_x, loupe_y), radius: R + 1.5, stroke: 1.0pt + col_focus.lighten(50%)) circle((loupe_x, loupe_y), radius: R + 3.5, stroke: 0.5pt + col_focus.lighten(80%)) }) } #let hldpc() = { import cetz.draw: * let points = ( (7, 0), (10, 0), (15, 0), (22, 0), (24, 0), (29, 0), (3, 1), (6, 1), (18, 1), (19, 1), (25, 1), (27, 1), (5, 2), (9, 2), (13, 2), (14, 2), (17, 2), (28, 2), (0, 3), (8, 3), (11, 3), (16, 3), (20, 3), (26, 3), (1, 4), (2, 4), (4, 4), (12, 4), (21, 4), (23, 4), (5, 5), (6, 5), (8, 5), (10, 5), (21, 5), (29, 5), (14, 6), (15, 6), (16, 6), (18, 6), (22, 6), (28, 6), (0, 7), (1, 7), (4, 7), (9, 7), (20, 7), (26, 7), (2, 8), (3, 8), (11, 8), (12, 8), (17, 8), (19, 8), (7, 9), (13, 9), (23, 9), (24, 9), (25, 9), (27, 9), (0, 10), (6, 10), (15, 10), (18, 10), (21, 10), (26, 10), (2, 11), (7, 11), (10, 11), (17, 11), (22, 11), (27, 11), (8, 12), (11, 12), (14, 12), (20, 12), (23, 12), (29, 12), (4, 13), (5, 13), (9, 13), (13, 13), (16, 13), (19, 13), (1, 14), (3, 14), (12, 14), (24, 14), (25, 14), (28, 14), ) cetz.canvas(length: 0.45cm, { let nx = 30 let ny = 15 let cell_size = 1.0 let h_width = nx * cell_size let h_height = ny * cell_size // Palette unifiée let color_blue = orange let color_orange = blue let col_dot_base = gray.darken(30%) // Définition des couleurs de fond (lighten) let col_row_bg = color_blue.lighten(90%) let col_col_bg = color_orange.lighten(90%) // Calcul du mélange pour l'intersection let col_mix_bg = col_row_bg.mix(col_col_bg) // Label "H =" content((-3.4, -h_height / 2), text(size: 1.6em, weight: "bold")[$bold(H) =$]) // Sélection visuelle (Focus) let sel_row = 7 let sel_col = 10 // Rectangles de fond rect((0, -sel_row), (h_width, -sel_row - 1), fill: col_row_bg, stroke: none) rect((sel_col, 0), (sel_col + 1, -h_height), fill: col_col_bg, stroke: none) rect((sel_col, -sel_row), (sel_col + 1, -sel_row - 1), fill: col_mix_bg, stroke: none) // Grands crochets matriciels let b_w = 0.6 set-style(stroke: (thickness: 1.5pt, cap: "round")) line((b_w, 0.3), (0, 0.3), (0, -h_height - 0.3), (b_w, -h_height - 0.3)) line((h_width - b_w, 0.3), (h_width, 0.3), (h_width, -h_height - 0.3), (h_width - b_w, -h_height - 0.3)) // Dessin des points for (x, y) in points { let px = x + 0.5 let py = -y - 0.5 let is_row = (y == sel_row) let is_col = (x == sel_col) let d_col = col_dot_base let r = 0.16 if is_row { d_col = color_blue r = 0.24 } else if is_col { d_col = color_orange r = 0.24 } circle((px, py), radius: r, fill: d_col, stroke: none) } // 5. Légendes w_r et w_c content((h_width + 0.5, -sel_row - 0.5), anchor: "west", text( fill: color_blue, weight: "bold", size: 1.1em, )[$w_r = 6$]) content((sel_col + 0.5, 0.8), anchor: "south", text(fill: color_orange, weight: "bold", size: 1.1em)[$w_c = 3$]) }) } #let hldpc_dual(row1: 0, row2: 14) = { import cetz.draw: * let points = ( (7, 0), (10, 0), (15, 0), (22, 0), (24, 0), (29, 0), (3, 1), (6, 1), (18, 1), (19, 1), (25, 1), (27, 1), (5, 2), (9, 2), (13, 2), (14, 2), (17, 2), (28, 2), (0, 3), (8, 3), (11, 3), (16, 3), (20, 3), (26, 3), (1, 4), (2, 4), (4, 4), (12, 4), (21, 4), (23, 4), (5, 5), (6, 5), (8, 5), (10, 5), (21, 5), (29, 5), (14, 6), (15, 6), (16, 6), (18, 6), (22, 6), (28, 6), (0, 7), (1, 7), (4, 7), (9, 7), (20, 7), (26, 7), (2, 8), (3, 8), (11, 8), (12, 8), (17, 8), (19, 8), (7, 9), (13, 9), (23, 9), (24, 9), (25, 9), (27, 9), (0, 10), (6, 10), (15, 10), (18, 10), (21, 10), (26, 10), (2, 11), (7, 11), (10, 11), (17, 11), (22, 11), (27, 11), (8, 12), (11, 12), (14, 12), (20, 12), (23, 12), (29, 12), (4, 13), (5, 13), (9, 13), (13, 13), (16, 13), (19, 13), (1, 14), (3, 14), (12, 14), (24, 14), (25, 14), (28, 14), ) cetz.canvas(length: 0.35cm, { let nx = 30 let ny = 15 let col_1 = orange let col_2 = blue content((-3.4, -7.5), text(size: 1.6em)[$H =$]) // Fonds de lignes rect((0, -row1), (nx, -row1 - 1), fill: col_1.lighten(90%), stroke: none) if row2 != none { rect((0, -row2), (nx, -row2 - 1), fill: col_2.lighten(90%), stroke: none) } // Crochets set-style(stroke: (thickness: 1.2pt)) line((0.5, 0.3), (0, 0.3), (0, -ny - 0.3), (0.5, -ny - 0.3)) line((nx - 0.5, 0.3), (nx, 0.3), (nx, -ny - 0.3), (nx - 0.5, -ny - 0.3)) for (x, y) in points { let d_col = gray.darken(30%) let r = 0.15 if y == row1 { d_col = col_1 r = 0.22 } else if y == row2 { d_col = col_2 r = 0.22 } circle((x + 0.5, -y - 0.5), radius: r, fill: d_col, stroke: none) } }) } // Données de H partagées (mêmes points que hldpc / hldpc_dual) #let _h_pts = ( (7, 0), (10, 0), (15, 0), (22, 0), (24, 0), (29, 0), (3, 1), (6, 1), (18, 1), (19, 1), (25, 1), (27, 1), (5, 2), (9, 2), (13, 2), (14, 2), (17, 2), (28, 2), (0, 3), (8, 3), (11, 3), (16, 3), (20, 3), (26, 3), (1, 4), (2, 4), (4, 4), (12, 4), (21, 4), (23, 4), (5, 5), (6, 5), (8, 5), (10, 5), (21, 5), (29, 5), (14, 6), (15, 6), (16, 6), (18, 6), (22, 6), (28, 6), (0, 7), (1, 7), (4, 7), (9, 7), (20, 7), (26, 7), (2, 8), (3, 8), (11, 8), (12, 8), (17, 8), (19, 8), (7, 9), (13, 9), (23, 9), (24, 9), (25, 9), (27, 9), (0, 10), (6, 10), (15, 10), (18, 10), (21, 10), (26, 10), (2, 11), (7, 11), (10, 11), (17, 11), (22, 11), (27, 11), (8, 12), (11, 12), (14, 12), (20, 12), (23, 12), (29, 12), (4, 13), (5, 13), (9, 13), (13, 13), (16, 13), (19, 13), (1, 14), (3, 14), (12, 14), (24, 14), (25, 14), (28, 14), ) #let tanner_illustration() = { cetz.canvas(length: 1.1cm, { import cetz.draw: * let vy = 2.6 let cy = 0.0 let vxs = (0.0, 1.2, 2.4, 3.6) let cxs = (0.9, 2.7) let edges = ((0, 0), (1, 0), (2, 0), (1, 1), (2, 1), (3, 1)) // Dessin des arêtes for (vi, ci) in edges { line( (vxs.at(vi), vy - 0.28), (cxs.at(ci), cy + 0.32), stroke: 0.9pt + gray.darken(10%), ) } // Nœuds de contrôle (carrés, orange) let cnames = ([$c_0$], [$c_1$]) for j in range(2) { let cx = cxs.at(j) let s = 0.30 rect( (cx - s, cy - s), (cx + s, cy + s), fill: orange.lighten(75%), stroke: 1.8pt + orange, name: "c" + str(j), ) content( (cx, cy - s - 0.3), anchor: "north", text(size: 0.8em, fill: orange, weight: "bold")[#cnames.at(j)], ) } // Nœuds de variable (cercles, bleu) let vnames = ([$v_0$], [$v_1$], [$v_2$], [$v_3$]) for i in range(4) { let vx = vxs.at(i) let r = 0.28 circle( (vx, vy), radius: r, fill: blue.lighten(75%), stroke: 1.8pt + blue, name: "v" + str(i), ) content( (vx, vy + r + 0.25), anchor: "south", text(size: 0.8em, fill: blue, weight: "bold")[#vnames.at(i)], ) } }) } // Mini matrice H avec surbrillance d'une ligne et/ou colonne #let h_mini_tanner(hl_row: none, hl_col: none) = { let pts = _h_pts cetz.canvas(length: 0.215cm, { import cetz.draw: * let nx = 30 let ny = 15 content((-7, -7.5), text(size: 1.5em, weight: "bold")[$H =$]) if hl_row != none { rect((0, -hl_row), (nx, -hl_row - 1), fill: orange.lighten(85%), stroke: none) } if hl_col != none { rect((hl_col, 0), (hl_col + 1, -ny), fill: blue.lighten(85%), stroke: none) } set-style(stroke: (thickness: 0.9pt, cap: "round")) line((0.4, 0.25), (0, 0.25), (0, -ny - 0.25), (0.4, -ny - 0.25)) line((nx - 0.4, 0.25), (nx, 0.25), (nx, -ny - 0.25), (nx - 0.4, -ny - 0.25)) for (x, y) in pts { let r = 0.13 let col = gray.darken(25%) if hl_row != none and y == hl_row { col = orange r = 0.22 } else if hl_col != none and x == hl_col { col = blue r = 0.22 } circle((x + 0.5, -y - 0.5), radius: r, fill: col, stroke: none) } if hl_row != none { content((nx + 0.5, -hl_row - 0.5), anchor: "west", text(fill: orange, size: 1.0em, weight: "bold")[$c_#hl_row$]) } if hl_col != none { content((hl_col + 0.5, 0.70), anchor: "south", text(fill: blue, size: 1.0em, weight: "bold")[$v_#hl_col$]) } }) } #let hldpc_dynamic(hl_rows: (), hl_cols: (), show_labels: true, h_show: true) = { import cetz.draw: * let points = ( (7, 0), (10, 0), (15, 0), (22, 0), (24, 0), (29, 0), (3, 1), (6, 1), (18, 1), (19, 1), (25, 1), (27, 1), (5, 2), (9, 2), (13, 2), (14, 2), (17, 2), (28, 2), (0, 3), (8, 3), (11, 3), (16, 3), (20, 3), (26, 3), (1, 4), (2, 4), (4, 4), (12, 4), (21, 4), (23, 4), (5, 5), (6, 5), (8, 5), (10, 5), (21, 5), (29, 5), (14, 6), (15, 6), (16, 6), (18, 6), (22, 6), (28, 6), (0, 7), (1, 7), (4, 7), (9, 7), (20, 7), (26, 7), (2, 8), (3, 8), (11, 8), (12, 8), (17, 8), (19, 8), (7, 9), (13, 9), (23, 9), (24, 9), (25, 9), (27, 9), (0, 10), (6, 10), (15, 10), (18, 10), (21, 10), (26, 10), (2, 11), (7, 11), (10, 11), (17, 11), (22, 11), (27, 11), (8, 12), (11, 12), (14, 12), (20, 12), (23, 12), (29, 12), (4, 13), (5, 13), (9, 13), (13, 13), (16, 13), (19, 13), (1, 14), (3, 14), (12, 14), (24, 14), (25, 14), (28, 14), ) cetz.canvas(length: 0.4cm, { let nx = 30 let ny = 15 let col_row = orange let col_col = blue let col_dot_base = gray.darken(30%) // Marge pour réduire l'épaisseur des bandes (crée le gap visuel) let gap = 0.1 if h_show { content((-3.4, -ny / 2), text(size: 1.6em, weight: "bold")[$H =$]) } else { content((-3.4, -ny / 2), text(size: 1.6em, weight: "bold")[$space space space$]) } // 1. Dessiner les fonds des lignes (avec gap) for r in hl_rows { rect((0, -r - gap), (nx, -r - 1 + gap), fill: col_row.lighten(85%), stroke: none) } // 2. Dessiner les fonds des colonnes (avec gap) for c in hl_cols { rect((c + gap, 0), (c + 1 - gap, -ny), fill: col_col.lighten(85%), stroke: none) } // 3. Dessiner les intersections for r in hl_rows { for c in hl_cols { rect( (c + gap, -r - gap), (c + 1 - gap, -r - 1 + gap), fill: col_row.lighten(85%).mix(col_col.lighten(85%)), stroke: none, ) } } // 4. Les grands crochets matriciels let b_w = 0.6 set-style(stroke: (thickness: 1.5pt, cap: "round")) line((b_w, 0.3), (0, 0.3), (0, -ny - 0.3), (b_w, -ny - 0.3)) line((nx - b_w, 0.3), (nx, 0.3), (nx, -ny - 0.3), (nx - b_w, -ny - 0.3)) // 5. Dessin des points for (x, y) in points { let px = x + 0.5 let py = -y - 0.5 let is_row = y in hl_rows let is_col = x in hl_cols let d_col = col_dot_base let r = 0.15 // Détermine la couleur et la taille si le point est dans une zone surlignée if is_row and is_col { d_col = col_row.mix(col_col) // Point à l'intersection r = 0.24 } else if is_row { d_col = col_row r = 0.24 } else if is_col { d_col = col_col r = 0.24 } circle((px, py), radius: r, fill: d_col, stroke: none) } // 6. Affichage optionnel des labels if show_labels { for r in hl_rows { content((nx + 0.5, -r - 0.5), anchor: "west", text(fill: col_row, weight: "bold", size: 1.1em)[$c_#r$]) } for c in hl_cols { content((c + 0.5, -ny - 0.8), anchor: "north", text(fill: col_col, weight: "bold", size: 1.1em)[$v_#c$]) } } }) } // Graphe de Tanner paramétrable // scale : taille d'une unité cetz // hl_row : index du check node à mettre en évidence (none = aucun) // hl_col : index du variable node à mettre en évidence (none = aucun) // show_all : afficher toutes les arêtes // colored : colorier tous les nœuds (bleu/orange) même sans highlight #let tanner_canvas( scale: 0.43cm, hl_row: none, hl_col: none, show_all: false, colored: false, v_c_show: true, ) = { let pts = _h_pts cetz.canvas(length: scale, { import cetz.draw: * let n_var = 30 let n_chk = 15 let vy = 5.5 let cy = 0.0 // --- Logique de centrage --- let gap_v = 1.0 // Espacement des cercles let gap_c = 1.8 // Espacement des carrés (un peu plus large pour l'équilibre visuel) let width_v = (n_var - 1) * gap_v let width_c = (n_chk - 1) * gap_c let offset_c = (width_v - width_c) / 2 // Calcul du décalage pour centrer C sous V // Noeuds connectés au check ou variable mis en évidence let hl_var_set = if hl_row != none { pts.filter(p => p.at(1) == hl_row).map(p => p.at(0)) } else { () } let hl_chk_set = if hl_col != none { pts.filter(p => p.at(0) == hl_col).map(p => p.at(1)) } else { () } // — ARÊTES — for (vj, ci) in pts { let vx = vj * gap_v let ccx = offset_c + (ci * gap_c) let is_row = hl_row != none and ci == hl_row let is_col = hl_col != none and vj == hl_col let do_draw = is_row or is_col or show_all if do_draw { let s = if is_row { 1.5pt + orange } else if is_col { 2.0pt + blue } else { 0.50pt + gray.lighten(20%) } line((vx, vy), (ccx, cy), stroke: s) } } // — NŒUDS DE CONTRÔLE (carrés) — for j in range(n_chk) { let ccx = offset_c + (j * gap_c) let sz = 0.40 let is_main = hl_row != none and j == hl_row let is_conn = hl_chk_set.contains(j) let fc = if is_main { orange.lighten(48%) } else if is_conn { blue.lighten(60%) } else if colored { orange.lighten(76%) } else { white } let sc = if is_main { 1.8pt + orange } else if is_conn { 1.8pt + blue } else if colored { 1.2pt + orange } else { 1.0pt + black } rect((ccx - sz, cy - sz), (ccx + sz, cy + sz), fill: fc, stroke: sc) if is_main { content((ccx, cy - sz - 0.33), anchor: "north", text(size: 0.52em, fill: orange, weight: "bold")[$c_#j$]) } else if is_conn { content((ccx, cy - sz - 0.33), anchor: "north", text(size: 0.52em, fill: blue, weight: "bold")[$c_#j$]) } } // — NŒUDS DE VARIABLE (cercles) — for i in range(n_var) { let vx = i * gap_v let r = 0.33 let is_main = hl_col != none and i == hl_col let is_conn = hl_var_set.contains(i) let fc = if is_main { blue.lighten(48%) } else if is_conn { orange.lighten(60%) } else if colored { blue.lighten(76%) } else { white } let sc = if is_main { 1.5pt + blue } else if is_conn { 1.5pt + orange } else if colored { 1.2pt + blue } else { 1.0pt + black } circle((vx, vy), radius: r, fill: fc, stroke: sc) if is_main { content((vx, vy + r + 0.30), anchor: "south", text(size: 0.52em, fill: blue, weight: "bold")[$v_#i$]) } else if is_conn { content((vx, vy + r + 0.30), anchor: "south", text(size: 0.52em, fill: orange, weight: "bold")[$v_#i$]) } } // Légendes des couches if v_c_show { content((-2.0, vy), anchor: "east", text(size: 0.65em, weight: "bold")[$V$]) content((-2.0, cy), anchor: "east", text(size: 0.65em, weight: "bold")[$C$]) } }) } // Diagramme passage de messages (Belief Propagation) #let bp_diagram() = { cetz.canvas(length: 1.05cm, { import cetz.draw: * let vy = 3.0 let cy = 0.0 let vxs = (0.0, 1.3, 2.6, 3.9) let cxs = (0.65, 2.6) let edges = ((0, 0), (1, 0), (2, 0), (1, 1), (2, 1), (3, 1)) let mk_bl = (end: "stealth", fill: blue, size: 0.17) let mk_or = (end: "stealth", fill: orange, size: 0.17) // Flèches bidirectionnelles décalées for (vi, ci) in edges { let vx = vxs.at(vi) let cx = cxs.at(ci) // V → C (bleu, décalé à gauche) line((vx - 0.06, vy - 0.32), (cx - 0.06, cy + 0.38), mark: mk_bl, stroke: 1.4pt + blue.lighten(15%)) // C → V (orange, décalé à droite) line((cx + 0.06, cy + 0.38), (vx + 0.06, vy - 0.32), mark: mk_or, stroke: 1.4pt + orange.lighten(10%)) } // Nœuds de variable let vnames = ([$v_0$], [$v_1$], [$v_2$], [$v_3$]) for i in range(4) { let vx = vxs.at(i) let r = 0.30 circle((vx, vy), radius: r, fill: blue.lighten(72%), stroke: 1.6pt + blue) content((vx, vy + r + 0.20), anchor: "south", text(size: 0.70em, fill: blue, weight: "bold")[#vnames.at(i)]) } // Nœuds de contrôle let cnames = ([$c_0$], [$c_1$]) for j in range(2) { let cx = cxs.at(j) let s = 0.33 rect((cx - s, cy - s), (cx + s, cy + s), fill: orange.lighten(72%), stroke: 1.6pt + orange) content((cx, cy - s - 0.22), anchor: "north", text(size: 0.70em, fill: orange, weight: "bold")[#cnames.at(j)]) } // Légendes des flèches content((4.45, vy - 0.5), anchor: "west", text(size: 0.65em, fill: blue, weight: "bold")[$mu_(j arrow i)$]) content((4.45, cy + 0.4), anchor: "west", text(size: 0.65em, fill: orange, weight: "bold")[$nu_(i arrow j)$]) }) } #let icon_var = box(baseline: 20%)[ #cetz.canvas({ import cetz.draw: * circle((0, 0), radius: 0.35em, fill: blue.lighten(88%), stroke: 1.5pt + blue) }) ] #let icon_chk = box(baseline: 20%)[ #cetz.canvas({ import cetz.draw: * rect((-0.35em, -0.35em), (0.35em, 0.35em), fill: orange.lighten(88%), stroke: 1.5pt + orange) }) ] // Calcule la parité d'un Check Node i en fonction des Variable Nodes connectés #let calcule_parite(v_values, index_chk) = { let neighbors = _h_pts.filter(p => p.at(1) == index_chk).map(p => p.at(0)) let somme = 0 for n in neighbors { somme = somme + v_values.at(n) } calc.rem(somme, 2) } #let tanner_status( scale: 0.43cm, v_values: none, hl_v: (), highlight_edges: false, ) = { let pts = _h_pts cetz.canvas(length: scale, { import cetz.draw: * let n_var = 30 let n_chk = 15 let vy = 5.5 let cy = 0.0 let gap_v = 1.0 let gap_c = 1.8 let width_v = (n_var - 1) * gap_v let width_c = (n_chk - 1) * gap_c let offset_c = (width_v - width_c) / 2 let col_ok = green let col_err = red for (vj, ci) in pts { let vx = vj * gap_v let ccx = offset_c + (ci * gap_c) let p_ci = calcule_parite(v_values, ci) let is_err_edge = highlight_edges and hl_v.contains(vj) and p_ci == 1 if not is_err_edge { line((vx, vy), (ccx, cy), stroke: 0.4pt + gray.lighten(60%)) } } for (vj, ci) in pts { let vx = vj * gap_v let ccx = offset_c + (ci * gap_c) let p_ci = calcule_parite(v_values, ci) let is_err_edge = highlight_edges and hl_v.contains(vj) and p_ci == 1 if is_err_edge { line((vx, vy), (ccx, cy), stroke: 1.5pt + col_err) } } for j in range(n_chk) { let ccx = offset_c + (j * gap_c) let sz = 0.40 let p = calcule_parite(v_values, j) let is_ok = (p == 0) rect( (ccx - sz, cy - sz), (ccx + sz, cy + sz), fill: if is_ok { col_ok.lighten(92%) } else { col_err.lighten(92%) }, stroke: if is_ok { 1.2pt + col_ok } else { 1.5pt + col_err }, ) content((ccx, cy), text(size: 0.65em, weight: "bold", fill: if is_ok { col_ok } else { col_err })[#p]) } for i in range(n_var) { let vx = i * gap_v let r = 0.33 let val = v_values.at(i) let is_err = hl_v.contains(i) circle( (vx, vy), radius: r, fill: if is_err { col_err.lighten(92%) } else { blue.lighten(92%) }, stroke: if is_err { 1.5pt + col_err } else { 1.2pt + blue }, ) content((vx, vy), text(size: 0.55em, weight: "bold", fill: if is_err { col_err } else { blue })[#val]) } }) } // Composant de zoom d'une seule contrainte (CN) et ses VN voisins #let zoom_contrainte(is_ok: true) = { cetz.canvas(length: 1cm, { import cetz.draw: * let col = if is_ok { green } else { red } rect((-2.2, -1.2), (2.2, 2.0), stroke: none, fill: none) let blue_fill = blue.lighten(90%) let blue_stroke = 1.2pt + blue let red_fill = red.lighten(90%) let red_stroke = 2pt + red let gray_stroke = 0.8pt + gray for i in (0, 1, 2) { let x = i * 1.5 - 1.5 let is_failed_vn = not is_ok and i == 1 line((x, 1.5), (0, 0), stroke: if is_failed_vn { red_stroke } else { gray_stroke }) } for i in (0, 1, 2) { let x = i * 1.5 - 1.5 let is_failed_vn = not is_ok and i == 1 circle( (x, 1.5), radius: 0.3, stroke: if is_failed_vn { red_stroke } else { blue_stroke }, fill: if is_failed_vn { red_fill } else { blue_fill }, ) let val = if is_failed_vn { "1" } else { "0" } content((x, 1.5), text(size: 12pt, fill: if is_failed_vn { col } else { blue }, weight: "bold")[#val]) } rect( (-0.3, -0.3), (0.3, 0.3), stroke: if not is_ok { red_stroke } else { 2pt + col }, fill: if not is_ok { red_fill } else { col.lighten(90%) }, ) content((0, 0), text(fill: col, weight: "bold", size: 15pt)[#(if is_ok { "0" } else { "1" })]) let eq_size = 14pt if is_ok { content( (0, -0.7), text(fill: col, size: eq_size)[$0 plus.o 0 plus.o 0 = bold(0)$], anchor: "north", ) } else { content( (0, -0.71), text(fill: col, size: eq_size)[$0 plus.o 1 plus.o 0 = bold(1)$], anchor: "north", ) } }) } // --- Schémas pour la Topologie et la Construction --- // Visualisation du Girth et d'un cycle de longueur 4 #let schema_girth_4(highlight: false) = { cetz.canvas(length: 0.8cm, { import cetz.draw: * let s_dash = (paint: red, thickness: 2pt, dash: "dashed") // Positionnement let v0 = (-1.5, 2) let v1 = (1.5, 2) let c0 = (-1.5, 0) let c1 = (1.5, 0) // Arêtes line(v0, c0, v1, c1, v0, stroke: if highlight { s_dash } else { 1pt + gray }) // line(v0, c1, stroke: 1pt + gray.lighten(60%)) // Noeuds circle(v0, radius: 0.3, fill: blue.lighten(90%), stroke: 1.2pt + blue) circle(v1, radius: 0.3, fill: blue.lighten(90%), stroke: 1.2pt + blue) rect( (c0.at(0) - 0.3, c0.at(1) - 0.3), (c0.at(0) + 0.3, c0.at(1) + 0.3), fill: orange.lighten(90%), stroke: 1.2pt + orange, ) rect( (c1.at(0) - 0.3, c1.at(1) - 0.3), (c1.at(0) + 0.3, c1.at(1) + 0.3), fill: orange.lighten(90%), stroke: 1.2pt + orange, ) // if highlight { // content( // (0, 1), // text(fill: red, weight: "bold", size: 0.8em)[Cycle $L=4$], // frame: "rect", // stroke: none, // fill: rgb("ffffffcc"), // ) // } }) } #let echo_chamber() = { cetz.canvas(length: 1cm, { import cetz.draw: * let v0 = (0, 2) let c0 = (0, 0) line(v0, c0, mark: (end: "stealth", fill: red), stroke: 2pt + red) arc((0, 0), start: -45deg, stop: 225deg, radius: 0.5, mark: (end: "stealth", fill: red), stroke: 1.5pt + red) circle(v0, radius: 0.3, fill: blue.lighten(90%), stroke: 1.5pt + blue) rect((-0.3, -0.3), (0.3, 0.3), fill: orange.lighten(90%), stroke: 1.5pt + orange) content((1, 1), text(fill: red, size: 0.8em, style: "italic")[Auto-renforcement \ d'une erreur], anchor: "west") }) } #let peg_concept() = { cetz.canvas(length: 0.6cm, { import cetz.draw: * circle((0, 4), radius: 0.4, fill: blue.lighten(80%), stroke: 1.5pt + blue) let targets = ((-3, 0), (0, 0), (3, 0)) for t in targets { line((0, 4), t, stroke: 1.5pt + green) rect( (t.at(0) - 0.3, t.at(1) - 0.3), (t.at(0) + 0.3, t.at(1) + 0.3), fill: orange.lighten(90%), stroke: 1.2pt + orange, ) } content((0, -1.2), text(size: 0.8em, fill: green, weight: "bold")[Distance maximale], anchor: "north") }) } // --- VRAIES MATRICES GÉNÉRÉES --- #let _h_pts = ( (10, 0), (14, 0), (15, 0), (16, 0), (17, 0), (2, 1), (5, 1), (7, 1), (13, 1), (16, 1), (18, 1), (21, 1), (28, 1), (4, 2), (9, 2), (15, 2), (17, 2), (18, 2), (3, 3), (13, 3), (17, 3), (25, 3), (29, 3), (1, 4), (2, 4), (9, 4), (29, 4), (5, 5), (7, 5), (11, 5), (14, 5), (16, 5), (22, 5), (27, 5), (6, 6), (14, 6), (22, 6), (28, 6), (1, 7), (11, 7), (12, 7), (21, 7), (23, 7), (0, 8), (5, 8), (23, 8), (26, 8), (27, 8), (1, 9), (3, 9), (4, 9), (6, 9), (7, 9), (8, 9), (11, 9), (19, 9), (21, 9), (26, 9), (12, 10), (15, 10), (20, 10), (29, 10), (2, 11), (12, 11), (20, 11), (24, 11), (28, 11), (0, 12), (3, 12), (8, 12), (10, 12), (18, 12), (19, 12), (24, 12), (25, 12), (4, 13), (8, 13), (20, 13), (22, 13), (23, 13), (26, 13), (0, 14), (6, 14), (9, 14), (10, 14), (13, 14), (19, 14), (24, 14), (25, 14), (27, 14), ) #let _p_pts = ( (0, 0), (1, 0), (3, 0), (4, 0), (7, 0), (8, 0), (10, 0), (13, 0), (14, 0), (2, 1), (3, 1), (9, 1), (10, 1), (11, 1), (12, 1), (13, 1), (0, 2), (1, 2), (2, 2), (4, 2), (7, 2), (8, 2), (9, 2), (10, 2), (11, 2), (12, 2), (2, 3), (3, 3), (4, 3), (11, 3), (12, 3), (13, 3), (14, 3), (0, 4), (2, 4), (3, 4), (5, 4), (7, 4), (9, 4), (11, 4), (14, 4), (0, 5), (2, 5), (3, 5), (4, 5), (7, 5), (8, 5), (9, 5), (10, 5), (12, 5), (13, 5), (14, 5), (2, 6), (3, 6), (8, 6), (9, 6), (11, 6), (3, 7), (4, 7), (5, 7), (6, 7), (7, 7), (8, 7), (10, 7), (11, 7), (13, 7), (1, 8), (2, 8), (3, 8), (8, 8), (9, 8), (12, 8), (0, 9), (5, 9), (9, 9), (11, 9), (0, 10), (3, 10), (4, 10), (7, 10), (8, 10), (10, 10), (11, 10), (12, 10), (13, 10), (0, 11), (1, 11), (3, 11), (4, 11), (5, 11), (6, 11), (11, 11), (14, 11), (2, 12), (4, 12), (6, 12), (8, 12), (9, 12), (11, 12), (12, 12), (14, 12), (3, 13), (4, 13), (5, 13), (7, 13), (8, 13), (10, 13), (11, 13), (13, 13), (14, 13), (1, 14), (2, 14), (9, 14), ) // --- Composant d'illustration des vraies densités --- #let paradoxe_densite_reel() = { cetz.canvas(length: 0.18cm, { // Réglage de l'échelle pour que ça tienne import cetz.draw: * // Fonction pour dessiner une matrice à partir de ses points let draw_real_matrix(offset_x, cols, rows, pts, title, col, dens_text) = { // Cadre rect((offset_x, 0), (offset_x + cols, -rows), stroke: 1.5pt + black) // Titre au dessus content((offset_x + cols / 2, 2.5), text(weight: "bold", size: 0.8em)[#title]) // Densité en dessous content((offset_x + cols / 2, -rows - 2.2), text( fill: gray, weight: "bold", size: 0.65em, )[_Densité_ : #dens_text]) // Dessin des vrais points (les '1') for (x, y) in pts { circle((offset_x + x + 0.5, -y - 0.5), radius: 0.35, fill: col, stroke: none) } } // --- 1. Dessin de la matrice H (0 à 30) --- // draw_real_matrix(0, 30, 15, _h_pts, "H (Clairsemée)", orange, "20%") draw_real_matrix(0, 30, 15, _h_pts, "H", orange, "20%") // --- 2. Zone centrale élargie pour le Pivot de Gauss --- let arrow_start = 32 let arrow_end = 50 let center_x = (arrow_start + arrow_end) / 2 line((arrow_start, -7.5), (arrow_end, -7.5), mark: (end: "stealth", fill: black), stroke: 1.5pt + black) content((center_x, -5), text(size: 0.70em, style: "italic", weight: "bold")[Pivot de Gauss]) // --- 3. Dessin de la matrice P décalée --- // On commence P à 52 (au lieu de 41) // draw_real_matrix(52, 15, 15, _p_pts, "Bloc P (Dense)", blue, "50%") draw_real_matrix(52, 15, 15, _p_pts, "Bloc P", blue, "50%") }) } #let schema_systematique() = { cetz.canvas(length: 1cm, { import cetz.draw: * let w_p = 2.2 let w_i = 2.4 let h = 1.5 // --- MATRICE H = [P^T | I] --- group({ translate((-w_p - w_i - 1.5, 0)) content((-0.2, h / 2), $bold(H) =$, anchor: "east") // 1. Les fonds colorés (sans bordure) rect((0, 0), (w_p, h), fill: blue.lighten(80%), stroke: none) rect((w_p, 0), (w_p + w_i, h), fill: gray.lighten(90%), stroke: none) // 2. Les lignes (une seule par intersection) rect((0, 0), (w_p + w_i, h), stroke: 1.5pt + black) // Contour global line((w_p, 0), (w_p, h), stroke: 1pt + black) // Séparation propre content((w_p / 2, h / 2), $bold(P)^top$, text: (fill: blue.darken(20%), weight: "bold")) content((w_p + w_i / 2, h / 2), $bold(I)_(n-k)$) }) // Flèche centrale line((-1.0, h / 2), (0.7, h / 2), mark: (end: "stealth", fill: black), stroke: 1.5pt + black) // --- MATRICE G = [I | P] --- group({ translate((2.5, 0)) content((-0.2, h / 2), $bold(G) =$, anchor: "east") // 1. Les fonds colorés (sans bordure) rect((0, 0), (w_i, h), fill: gray.lighten(90%), stroke: none) rect((w_i, 0), (w_i + w_p, h), fill: blue.lighten(80%), stroke: none) // 2. Les lignes rect((0, 0), (w_i + w_p, h), stroke: 1.5pt + black) // Contour global line((w_i, 0), (w_i, h), stroke: 1pt + black) // Séparation propre content((w_i / 2, h / 2), $bold(I)_k$) content((w_i + w_p / 2, h / 2), $bold(P)$, text: (fill: blue.darken(20%), weight: "bold")) }) }) } #let dessiner_matrice(nom, blocs, hauteur: 1.5) = { cetz.canvas(length: 1cm, { import cetz.draw: * group({ // 1. Nom de la matrice à gauche (ex: "H =") if nom != none { content((-0.2, hauteur / 2), nom, anchor: "east") } let x = 0 // Curseur pour avancer de gauche à droite // 2. Étape des fonds et du texte (sans aucune bordure) for bloc in blocs { let w = bloc.at("largeur", default: 2) let bg = bloc.at("fond", default: none) let txt = bloc.at("texte", default: none) // Dessine le fond coloré rect((x, 0), (x + w, hauteur), fill: bg, stroke: none) // Place le texte exactement au milieu du bloc content((x + w / 2, hauteur / 2), txt) x += w // On décale le curseur pour le prochain bloc } let largeur_totale = x // 3. Étape des lignes (pour des intersections parfaites) x = 0 for i in range(blocs.len() - 1) { x += blocs.at(i).at("largeur", default: 2) // Lignes de séparation internes (plus fines) line((x, 0), (x, hauteur), stroke: 1pt + black) } // 4. Contour global par-dessus tout (plus épais) rect((0, 0), (largeur_totale, hauteur), stroke: 1.5pt + black, fill: none) }) }) } //Comp 1 #let vvals = (1, 0, 1, 0) #let edges = ((0, 0), (1, 0), (3, 0), (0, 1), (2, 1), (3, 1), (1, 2), (2, 2)) #let graph_layout(step: 1) = { cetz.canvas(length: 0.85cm, { import cetz.draw: * let (vy, cy) = (4.5, 0.0) // vy = Top (Variable Nodes), cy = Bottom (Check Nodes) let vxs = (0.0, 2.5, 5.0, 7.5) let cxs = (1.25, 3.75, 6.25) let r_v = 0.45 let s_c = 0.4 // --- 1. LES ARÊTES AVEC DIRECTIONALITÉ --- for (k, (vi, ci)) in edges.enumerate() { let v_pos = (vxs.at(vi), vy) let c_pos = (cxs.at(ci), cy) if step == 1 { // VN -> CN : On part du haut vers le bas line( v_pos, c_pos, shorten-start: r_v, shorten-end: s_c, mark: (end: ">", fill: blue, size: 0.3), stroke: 2pt + blue, ) } else if step == 2 { // CN -> VN : On part du bas vers le haut let is_err = (ci == 0 or ci == 2) let col = if is_err { red } else { green.darken(20%) } line( c_pos, v_pos, shorten-start: s_c, shorten-end: r_v, mark: (end: ">", fill: col, size: 0.3), stroke: 2pt + col, ) } else { // Étape 3 : Passif line(v_pos, c_pos, shorten-start: r_v, shorten-end: s_c, stroke: 1pt + gray.lighten(70%)) } } // --- 2. LES VARIABLE NODES (VN) --- for i in range(4) { let pos = (vxs.at(i), vy) let is_flip = (step == 3 and i == 1) let col = if is_flip { red } else { blue } let bg = if is_flip { red.lighten(92%) } else { blue.lighten(90%) } let val = if is_flip { "1" } else { str(vvals.at(i)) } circle(pos, radius: r_v, fill: bg, stroke: (if is_flip { 2.5pt } else { 1.5pt }) + col) content(pos, text(weight: "bold", fill: col, size: 1.1em)[#val]) content((pos.at(0), pos.at(1) + 0.8), text(size: 0.8em, fill: col.darken(0%))[$v_#i$]) if is_flip { content((pos.at(0) + 0.1, pos.at(1) + 1.3), text(fill: red, weight: "bold", size: 0.7em)[FLIP !]) } } // --- 3. LES CHECK NODES (CN) --- for j in range(3) { let pos = (cxs.at(j), cy) let val = if j == 1 { 0 } else { 1 } let col = orange let bg = orange.lighten(90%) if step >= 2 { let is_ok = (val == 0) col = if is_ok { green.darken(20%) } else { red } bg = col.lighten(92%) } rect((pos.at(0) - s_c, pos.at(1) - s_c), (pos.at(0) + s_c, pos.at(1) + s_c), fill: bg, stroke: 1.8pt + col) content(pos, text(weight: "bold", fill: col, size: 1.1em)[#str(val)]) content((pos.at(0), pos.at(1) - 0.8), text(size: 0.8em, fill: col.darken(0%))[$c_#j$]) } }) } #let bf_step1_sending() = graph_layout(step: 1) #let bf_step2_verdict() = graph_layout(step: 2) #let bf_step3_flip() = graph_layout(step: 3) #let schema_vote_majoritaire(n_erreurs: 3, flipped: true) = { cetz.canvas(length: 1.0cm, { import cetz.draw: * // Positions des CN autour du VN central let v_pos = (0.0, 0.0) let c_positions = ( (-2.2, 1.8), (0.0, 2.2), (2.2, 1.8), (-2.2, -1.8), ) // Statuts des CN (1=Erreur) let states = (1, 1, 1, 0) let col_err = red let col_ok = green.darken(10%) // Arêtes avec flèche CN→VN for (i, p) in c_positions.enumerate() { let col = if states.at(i) == 1 { col_err } else { col_ok } // VN→CN (bleu) line( v_pos, p, mark: (end: "stealth", fill: blue.lighten(20%), size: 0.16), stroke: 1.2pt + blue.lighten(30%), ) // CN→VN (coloré) line( p, (v_pos.at(0) + 0.06, v_pos.at(1) + 0.06), mark: (end: "stealth", fill: col, size: 0.18), stroke: 1.8pt + col, ) // Badge message let mx = (v_pos.at(0) + p.at(0)) / 2 let my = (v_pos.at(1) + p.at(1)) / 2 content( (mx, my), box( fill: if states.at(i) == 1 { red.lighten(88%) } else { green.lighten(88%) }, stroke: 0.5pt + col, radius: 2pt, inset: (x: 3pt, y: 1.5pt), )[#text(size: 0.52em, fill: col, weight: "bold")[#if states.at(i) == 1 { [✗] } else { [✓] }]], ) } // CN (carrés) for (i, p) in c_positions.enumerate() { let col = if states.at(i) == 1 { col_err } else { col_ok } let sz = 0.35 rect( (p.at(0) - sz, p.at(1) - sz), (p.at(0) + sz, p.at(1) + sz), fill: col.lighten(90%), stroke: 1.8pt + col, ) content(p, text(size: 0.72em, fill: col, weight: "bold")[#states.at(i)]) } // VN central let r_vn = 0.52 let col_vn = if flipped { red } else { blue } circle( v_pos, radius: r_vn, fill: col_vn.lighten(82%), stroke: 2.5pt + col_vn, ) let val_txt = if flipped { [0] } else { [1] } content(v_pos, text(size: 0.95em, fill: col_vn, weight: "bold")[#val_txt]) // Décision sous le nœud if flipped { content( (0, -1.0), box( fill: red.lighten(88%), stroke: 1.2pt + red, radius: 4pt, inset: (x: 7pt, y: 4pt), )[#text(fill: red, weight: "bold", size: 0.78em)[FLIP : 1 → 0]], ) } else { content( (0, -1.0), box( fill: rgb("#f8f9fa"), stroke: 0.8pt + gray, radius: 4pt, inset: (x: 7pt, y: 4pt), )[#text(fill: gray.darken(40%), size: 0.72em, style: "italic")[Majorité = 3/4 ✗]], ) } }) } // --- Définitions de style globales --- #let iterative_decoder_colors = ( top: rgb("000000"), // Noir cn_update: rgb("ff9933"), // Orange vn_update: rgb("3366cc"), // Bleu success: rgb("339900"), // Vert failure: rgb("cc3333"), // Rouge edge: black, ) // Définir des styles de nœuds avec de grands insets pour un espacement généreux #let node_style = ( cn_update: ( stroke: iterative_decoder_colors.cn_update, fill: iterative_decoder_colors.cn_update.lighten(90%), radius: 4pt, inset: (x: 20pt, y: 15pt), // Grand inset horizontal et vertical label: (align: center, gutter: 8pt, size: 1em), ), vn_update: ( stroke: iterative_decoder_colors.vn_update, fill: iterative_decoder_colors.vn_update.lighten(90%), radius: 4pt, inset: (x: 20pt, y: 15pt), label: (align: center, gutter: 8pt, size: 1em), ), diamond_node: ( radius: 1pt, // Coins légèrement arrondis pour le diamant label: (align: center, size: 1em, gutter: 0pt), // Font légèrement plus petit pour la lisibilité ), output_node: ( radius: 4pt, inset: (x: 10pt, y: 8pt), // Espacement légèrement plus compact pour les sorties label: (align: center, size: 1em, gutter: 4pt), ), ) // Style d'arête #let edge_style = ( stroke: iterative_decoder_colors.edge, mark: (end: ">"), ) #let schema_boucle_bf() = { cetz.canvas(length: 0.9cm, { import cetz.draw: * let s_fleche = (stroke: 1.5pt + black, mark: (end: "stealth", fill: black, size: 0.22)) let col_1 = orange let col_2 = blue let col_3 = green.darken(10%) let col_stop = red // --- 1. PARAMÈTRES --- let w_boite = 9.2 let h_boite = 3.2 let espacement_titre = 8pt // Un peu réduit pour l'équilibre let y_init = 12.0 let y_cn = 8.0 let y_vn = 3.5 let y_check = -1.0 let y_fail = -5.5 let w_l = 2.4 let h_l = 1.3 // --- 2. ANCRES (Rectangles invisibles) --- rect((-2.5, y_init - 0.6), (2.5, y_init + 0.6), stroke: none, name: "init") rect((-w_boite / 2, y_cn - h_boite / 2), (w_boite / 2, y_cn + h_boite / 2), stroke: none, name: "cn") rect((-w_boite / 2, y_vn - h_boite / 2), (w_boite / 2, y_vn + h_boite / 2), stroke: none, name: "vn") rect((-w_l, y_check - h_l), (w_l, y_check + h_l), stroke: none, name: "check") // Bloc succès bien large rect((5.8, y_check - 1.0), (12.5, y_check + 1.0), stroke: none, name: "ok") rect((-w_boite / 2, y_fail - 0.7), (w_boite / 2, y_fail + 0.7), stroke: none, name: "fail") // --- 3. DESSIN DES FLÈCHES --- line("init.south", "cn.north", ..s_fleche) line("cn.south", "vn.north", ..s_fleche) line("vn.south", "check.north", ..s_fleche) line("check.south", "fail.north", ..s_fleche) line("check.east", "ok.west", ..s_fleche) line("check.west", (-6.5, y_check), (-6.5, y_cn), "cn.west", ..s_fleche) // --- 4. DESSIN VISUEL --- // Mot reçu rect("init.north-west", "init.south-east", fill: black, radius: 25pt) content("init", text(white, weight: "bold", size: 1.1em)[Mot reçu $bold(r)$]) // ① CN Update - Correction de l'équilibre rect("cn.north-west", "cn.south-east", fill: col_1.lighten(97%), stroke: 2.2pt + col_1, radius: 5pt) content("cn")[ #set align(center + horizon) #block(breakable: false)[ #set align(left) #stack(dir: ttb, spacing: espacement_titre, text(1.1em, fill: col_1, weight: "bold")[① CN Update], text( 0.85em, )[Calcul du syndrome $bold(s) = bold(H)bold(r)^T$ \ Chaque $c_i$ évalue sa parité]) ] ] // ② VN Update - Correction de l'équilibre rect("vn.north-west", "vn.south-east", fill: col_2.lighten(97%), stroke: 2.2pt + col_2, radius: 5pt) content("vn")[ #set align(center + horizon) #block(breakable: false)[ #set align(left) #stack(dir: ttb, spacing: espacement_titre, text(1.1em, fill: col_2, weight: "bold")[② VN Update], text( 0.85em, )[Chaque $v_j$ compte ses erreurs\ Si majorité → FLIP]) ] ] // Losange line( "check.north", "check.east", "check.south", "check.west", close: true, fill: green.lighten(97%), stroke: 2.2pt + col_3, ) content("check", text(1.1em, weight: "bold")[$bold(s) = bold(0)$]) // Succès : Largeur corrigée et texte sur une ligne rect("ok.north-west", "ok.south-east", fill: col_3.lighten(97%), stroke: 2.2pt + col_3, radius: 5pt) content("ok", text(col_3, weight: "bold", size: 1.1em)[Succès : $hat(bold(c))=bold(r)$]) // Échec - Correction de l'équilibre rect("fail.north-west", "fail.south-east", fill: col_stop.lighten(97%), stroke: 2.2pt + col_stop, radius: 5pt) content("fail")[ #set align(center + horizon) #text(col_stop, weight: "bold", size: 1.1em)[iter $>$ M $arrow.r$ Échec] ] // --- 5. ÉTIQUETTES --- // Oui centré sur sa flèche content((4.1, y_check + 0.6), text(col_3, weight: "bold", size: 1.1em)[Oui]) content((1.2, (y_check + y_fail) / 2), text(col_stop, weight: "bold", size: 1.1em)[Non]) content((-7.3, (y_cn + y_check) / 2), angle: 90deg, text(size: 1.1em, style: "italic", weight: "bold")[iter $+1$]) }) } ///// // // // #let schema_llr_droite() = { cetz.canvas(length: 1cm, { import cetz.draw: * let col_left = blue.darken(10%) // Bit 0 (Inversé) let col_right = red.darken(10%) // Bit 1 (Inversé) let col_ax = black let col_mid = gray.darken(30%) let L = 7.0 // demi-longueur de l'axe let y0 = 0.0 let y_top = 1.8 // ── 1. Zones colorées (Dessinées en PREMIER, en HAUT) ── // Zone gauche (négative) -> Bit 0 rect((-L, y0 + 0.02), (-0.05, y0 + 0.32), fill: col_left.lighten(92%), stroke: none) content((-L / 2, y0 + 0.65), text(fill: col_left, weight: "bold", size: 0.9em)[Bit = 0]) // Zone droite (positive) -> Bit 1 rect((0.05, y0 + 0.02), (L, y0 + 0.32), fill: col_right.lighten(92%), stroke: none) content((L / 2, y0 + 0.65), text(fill: col_right, weight: "bold", size: 0.9em)[Bit = 1]) // ── 2. Axe horizontal (Dessiné APRÈS pour passer AU-DESSUS) ── line((-L, y0), (L, y0), stroke: 1.8pt + col_ax, mark: (end: "stealth", fill: col_ax, size: 0.22)) content((L + 0.5, y0), text(size: 0.85em)[$L(v_i)$], anchor: "west") // ── 3. Zéro ── line((0, -0.2), (0, 0.2), stroke: 1.5pt + col_mid) content((0, -0.5), text(size: 0.8em, fill: col_mid)[0]) // ── 4. Points exemples (Sans les Volts) ── // -6 (Bit 0 à gauche) let px_left = -5.0 circle((px_left, y0), radius: 0.15, fill: col_left, stroke: none) // La ligne pointillée part de l'axe vers le haut line((px_left, y0), (px_left, y_top), stroke: (paint: col_left, thickness: 1pt, dash: "dashed")) content( (px_left, y_top + 0.1), box(fill: col_left.lighten(92%), stroke: 0.5pt + col_left, radius: 3pt, inset: 4pt)[ #text(fill: col_left, weight: "bold", size: 0.75em)[$-6$] ], anchor: "south", ) // +0.3 (Proche de zéro, Bit 1) let px_near = 0.6 circle((px_near, y0), radius: 0.15, fill: col_right.lighten(40%), stroke: none) line((px_near, y0), (px_near, y_top - 0.7), stroke: (paint: col_right.lighten(20%), thickness: 1pt, dash: "dashed")) content( (px_near, y_top - 0.65), box(fill: white, stroke: 0.5pt + col_right.lighten(30%), radius: 3pt, inset: 3pt)[ #text(fill: col_right.darken(20%), size: 0.7em)[$+0.3$] ], anchor: "south-west", ) // +10 (Bit 1 à droite) let px_right = 5.8 circle((px_right, y0), radius: 0.15, fill: col_right, stroke: none) line((px_right, y0), (px_right, y_top), stroke: (paint: col_right, thickness: 1pt, dash: "dashed")) content( (px_right, y_top + 0.1), box(fill: col_right.lighten(92%), stroke: 0.5pt + col_right, radius: 3pt, inset: 4pt)[ #text(fill: col_right, weight: "bold", size: 0.75em)[$+10$] ], anchor: "south", ) }) } ///2 #let schema_sum_product_dual() = { cetz.canvas(length: 1cm, { import cetz.draw: * let col_cn = orange.darken(0%) let col_vn = blue.darken(0%) let col_msg = gray.darken(35%) // --- PARTIE HAUTE : MISE À JOUR CN (PRODUIT DES TANH) --- group({ let base_y = 4.0 let cn_pos = (0.0, base_y) let vn_in_pos = ((-3.2, base_y + 1.3), (-3.2, base_y - 1.3)) let vn_out_pos = (3.2, base_y) // 1. Check Node Central (cj) rect( (cn_pos.at(0) - 0.45, cn_pos.at(1) - 0.45), (cn_pos.at(0) + 0.45, cn_pos.at(1) + 0.45), fill: col_cn.lighten(90%), stroke: 2pt + col_cn, name: "cj_top", ) content("cj_top", text(size: 0.75em, fill: col_cn, weight: "bold")[$c_j$]) // 2. Variable Node de sortie (v0) circle(vn_out_pos, radius: 0.5, fill: white, stroke: 2pt + col_vn, name: "v0_top") content("v0_top", text(size: 0.7em, fill: col_vn, weight: "bold")[$v_0$]) // 3. Arête de sortie : cj -> v0 line("cj_top", "v0_top", stroke: 2.2pt + orange, mark: (end: "stealth", fill: orange, size: 0.25)) content((1.8, base_y + 0.6), text(size: 0.8em, fill: orange.darken(0%), weight: "bold")[$m_(c_j arrow v_0)$]) // 4. Voisins entrants (vi) for (i, p) in vn_in_pos.enumerate() { let n_name = "vin_" + str(i) circle(p, radius: 0.45, fill: col_vn.lighten(92%), stroke: 1.2pt + col_vn, name: n_name) content(n_name, text(size: 0.65em, fill: col_vn, weight: "bold")[$v_#(i + 1)$]) line(n_name, "cj_top", stroke: 1.1pt + col_msg, mark: (end: "stealth", fill: col_msg, size: 0.2)) content((p.at(0) + 1.4, p.at(1)), text(size: 0.75em, fill: col_msg)[$m_(v_#(i + 1) arrow c_j)$]) } }) // Séparateur discret line((-4.5, 1.8), (4.5, 1.8), stroke: (paint: gray.lighten(85%), dash: "dashed")) // --- PARTIE BASSE : MISE À JOUR VN (SOMME DES LLR) --- group({ let base_y = -1.5 let vn_pos = (0.0, base_y) let cn_in_pos = ((-3.2, base_y + 1.2), (-3.2, base_y - 1.2)) let cn_out_pos = (3.2, base_y) // 1. Variable Node Central (vj) circle(vn_pos, radius: 0.55, fill: col_vn.lighten(90%), stroke: 2pt + col_vn, name: "vj_bot") content("vj_bot", text(weight: "bold", fill: col_vn)[$v_j$]) // 2. Check Node de sortie (c0) - Toujours ORANGE rect( (cn_out_pos.at(0) - 0.4, cn_out_pos.at(1) - 0.4), (cn_out_pos.at(0) + 0.4, cn_out_pos.at(1) + 0.4), fill: white, stroke: 1.8pt + orange, name: "c0_bot", ) content("c0_bot", text(size: 0.65em, fill: orange.darken(0%), weight: "bold")[$c_0$]) // 3. Arête de sortie : vj -> c0 line("vj_bot", "c0_bot", stroke: 2.2pt + blue, mark: (end: "stealth", fill: blue, size: 0.25)) content((1.8, base_y - 0.7), text(size: 0.8em, fill: blue.darken(0%), weight: "bold")[$m_(v_j arrow c_0)$]) // 4. Entrée Canal LLR let canal_top = (0.0, base_y + 2.2) line(canal_top, "vj_bot", stroke: 1.8pt + blue, mark: (end: "stealth", fill: blue, size: 0.22)) content((canal_top.at(0), canal_top.at(1) + 0.3), text( size: 0.85em, fill: blue, weight: "bold", )[$L_"canal" (y_j)$]) // 5. Check Nodes entrants for (i, p) in cn_in_pos.enumerate() { let n_name = "cin_" + str(i) rect( (p.at(0) - 0.35, p.at(1) - 0.35), (p.at(0) + 0.35, p.at(1) + 0.35), fill: orange.lighten(95%), stroke: 1pt + orange, name: n_name, ) content(n_name, text(size: 0.6em, fill: orange.darken(0%))[$c_#(i + 1)$]) line(n_name, "vj_bot", stroke: 1.1pt + col_msg, mark: (end: "stealth", fill: col_msg, size: 0.2)) content((p.at(0) + 1.4, p.at(1)), text(size: 0.75em, fill: col_msg)[$m_(c_#(i + 1) arrow v_j)$]) } }) }) } #let col_cn = orange #let col_vn = blue #let col_msg = gray.darken(50%) #let s_len = 1.1cm // 1. INITIALISATION #let schema_detailed_init() = cetz.canvas(length: s_len, { import cetz.draw: * // Couleurs pures let col_vn_pure = blue let col_cn_pure = orange let col_msg_dark = gray.darken(50%) // Noeud Variable circle((0, 0), radius: 0.6, fill: col_vn_pure.lighten(90%), stroke: 2pt + col_vn_pure, name: "vj") content("vj", text(weight: "bold")[$v_j$]) // Entrée canal line((0, 1.8), (0, 0.6), stroke: 2pt + blue, mark: (end: "stealth", fill: blue, size: 0.2)) content((0, 2.1), text(fill: blue, size: 0.9em, weight: "bold")[$L_("canal")$]) // Targets plus éloignées sur la droite (x: 4.0) let targets = ((4.0, 1.2), (4.0, -1.2)) for (i, p) in targets.enumerate() { let name = "c" + str(i + 1) // Check Nodes rect( (p.at(0) - 0.4, p.at(1) - 0.4), (p.at(0) + 0.4, p.at(1) + 0.4), fill: col_cn_pure.lighten(95%), stroke: 1.5pt + col_cn_pure, name: name, ) content(name, text(size: 0.7em, fill: col_cn_pure, weight: "bold")[$c_#(i + 1)$]) // Arête line("vj.east", name, stroke: 1.2pt + col_msg_dark, mark: (end: "stealth", fill: col_msg_dark)) // Position du message calculée pour être "collée" à l'arête // On place le texte à mi-chemin (x=2.0) avec un léger offset y let y_offset = if p.at(1) > 0 { 0.4 } else { -0.25 } content((2.0, p.at(1) * 0.5 + y_offset), text(size: 0.75em, fill: col_msg_dark)[$m_(v_j arrow c_#(i + 1))$]) } }) // 2. CN UPDATE #let schema_detailed_cn() = cetz.canvas(length: s_len, { import cetz.draw: * rect((-0.5, -0.5), (0.5, 0.5), fill: col_cn.lighten(90%), stroke: 2.2pt + col_cn, name: "cj") content("cj", text(weight: "bold", fill: col_cn)[$c_j$]) let ins = ((-3.0, 1.3), (-3.0, -1.3)) for (i, p) in ins.enumerate() { let v_name = "vi" + str(i + 1) circle(p, radius: 0.45, fill: col_vn.lighten(92%), stroke: 1.2pt + col_vn, name: v_name) line(v_name, "cj", stroke: 1.1pt + col_msg, mark: (end: "stealth", fill: col_msg)) content(v_name, text(size: 0.65em, fill: col_vn, weight: "bold")[$v_#(i + 1)$]) content((p.at(0) + 1.5, p.at(1)), text(size: 0.75em, fill: col_msg)[$m_(v_#(i + 1) arrow c_j)$]) } circle((3.0, 0), radius: 0.6, fill: white, stroke: 2pt + col_vn, name: "v0") content("v0", text(size: 0.75em, fill: col_vn, weight: "bold")[$v_0$]) line("cj", "v0", stroke: 2.3pt + orange, mark: (end: "stealth", fill: orange, size: 0.25)) content((1.8, 0.6), text(size: 0.85em, fill: orange, weight: "bold")[$m_(c_j arrow v_0)$]) }) // 3. VN UPDATE #let schema_detailed_vn() = cetz.canvas(length: s_len, { import cetz.draw: * circle((0, 0), radius: 0.7, fill: col_vn.lighten(90%), stroke: 2.5pt + col_vn, name: "vj") content("vj", text(weight: "bold", fill: col_vn)[$v_j$]) line((0, 2.0), (0, 0.7), stroke: 2pt + blue, mark: (end: "stealth", fill: blue, size: 0.2)) content((0, 2.3), text(size: 0.85em, fill: blue, weight: "bold")[$L_("canal")$]) let ins = ((-3.0, 1.2), (-3.0, -1.2)) for (i, p) in ins.enumerate() { let c_name = "ci" + str(i + 1) rect( (p.at(0) - 0.4, p.at(1) - 0.4), (p.at(0) + 0.4, p.at(1) + 0.4), fill: col_cn.lighten(95%), stroke: 1.2pt + col_cn, name: c_name, ) line(c_name, "vj", stroke: 1.1pt + col_msg, mark: (end: "stealth", fill: col_msg)) content(c_name, text(size: 0.65em, fill: col_cn, weight: "bold")[$c_#(i + 1)$]) content((p.at(0) + 1.5, p.at(1)), text(size: 0.75em, fill: col_msg)[$m_(c_#(i + 1) arrow v_j)$]) } rect((2.6, -0.45), (3.4, 0.45), fill: white, stroke: 2pt + orange, name: "c0") content("c0", text(size: 0.7em, fill: col_cn, weight: "bold")[$c_0$]) line("vj", "c0", stroke: 2.3pt + blue, mark: (end: "stealth", fill: blue, size: 0.25)) content((1.7, -0.8), text(size: 0.85em, fill: blue, weight: "bold")[$m_(v_j arrow c_0)$]) }) // 4. DÉCISION FINALE #let schema_detailed_decision() = cetz.canvas(length: s_len, { import cetz.draw: * let col_vn_dec = green // 1. Variable Node Central (Le bit j) circle((0, 0), radius: 0.75, fill: green.lighten(92%), stroke: 2.5pt + col_vn_dec, name: "vn") content("vn", text(weight: "bold", size: 1.1em)[$Lambda_j$]) // 2. Entrée LLR Canal line((0, 1.7), (0, 0.75), stroke: 1.8pt + blue, mark: (end: "stealth", fill: blue, size: 0.15)) content((0, 2.0), text(fill: blue, size: 0.8em, weight: "bold")[$L_"canal"$]) // 3. Arrivées des Check Nodes (i) let ins = ((-2.3, 1.0), (-2.3, -1.0), (2.3, 1.0), (2.3, -1.0)) for (i, p) in ins.enumerate() { let name = "call" + str(i) rect( (p.at(0) - 0.3, p.at(1) - 0.3), (p.at(0) + 0.3, p.at(1) + 0.3), fill: orange.lighten(95%), stroke: 1.1pt + orange, name: name, ) line(name, "vn", stroke: 1.1pt + orange, mark: (end: "stealth", fill: orange, size: 0.12)) content(name, text(size: 0.5em, fill: orange, weight: "bold")[$c_#i$]) } // 4. ÉQUATION LAMBDA : On somme sur les voisins du bit j -> N(v_j) content((0, -2), text(size: 0.8em, fill: col_vn_dec)[ $display(Lambda_j = L_("canal") + sum_(i in cal(N)(v_j)) m_(c_i arrow v_j))$ ]) // 5. Bloc Hard Decision content((3.5, 0), name: "verdict", box(fill: white, stroke: 1.5pt + black, inset: 6pt, radius: 2pt)[ #set text(size: 0.6em) #set math.cases(gap: 0.3em) $hat(v)_j = cases(0 "si" Lambda_j > 0, 1 "si" Lambda_j < 0)$ ]) // 6. Liaison pointillés line((0.8, 0), "verdict.west", stroke: (paint: black, thickness: 1.3pt, dash: "dotted")) }) // ③ Schéma Min-Sum : comparaison SP vs MS + highlight du terme minimum // #let schema_min_sum_vis() = { // cetz.canvas(length: 1cm, { // import cetz.draw: * // // let col_ms = blue.darken(15%) // let col_min = red.darken(5%) // let col_sgn = orange.darken(5%) // // // Messages entrants (4 voisins u ≠ v) // let msgs = (2.1, 0.4, 3.7, 1.2) // let sgns = (1, -1, 1, 1) // signes // // let y_base = 0.0 // let xs = (-4.5, -1.5, 1.5, 4.5) // // // Titre colonnes // content((-3.0, 4.8), text(size: 0.82em, fill: col_sgn, weight: "bold")[Signes]) // content((3.0, 4.8), text(size: 0.82em, fill: col_ms, weight: "bold")[|Amplitudes|]) // // for (i, x) in xs.enumerate() { // let m = msgs.at(i) // let s = sgns.at(i) // let is_min = (m == 0.4) // let col_box = if is_min { col_min } else { col_ms } // // // Barre amplitude // let bar_h = m * 0.55 // rect((x - 0.5, y_base), (x + 0.5, y_base + bar_h), // fill: col_box.lighten(if is_min { 70% } else { 85% }), // stroke: 1.5pt + col_box) // // // Valeur amplitude // content((x, y_base + bar_h + 0.35), // text(size: 0.82em, fill: col_box, weight: "bold")[#m]) // // // Signe (XOR logic) // let s_txt = if s > 0 { [+] } else { [−] } // let s_col = col_sgn // content((x, -0.65), // box(fill: s_col.lighten(88%), stroke: 0.5pt + s_col, // radius: 2pt, inset: (x:6pt, y:3pt))[ // #text(size: 0.88em, fill: s_col, weight: "bold")[#s_txt] // ]) // // // Label u_i // content((x, -1.3), text(size: 0.74em, fill: gray.darken(30%))[ // $m_(#(i+1)→c)$ // ]) // } // // // Flèche vers résultat // let y_res = -2.4 // line((0, -1.6), (0, y_res + 0.3), // mark: (end: "stealth", fill: black, size: 0.2), // stroke: 1.4pt + black) // // // Encadré résultat // content((0, y_res - 0.1), // box(fill: col_min.lighten(88%), stroke: 1.5pt + col_min, // radius: 5pt, inset: (x: 14pt, y: 8pt))[ // #set text(size: 0.82em) // Signe produit : #text(fill: col_sgn, weight: "bold")[$(-1)^1 = -$] // #h(8pt) // Min : #text(fill: col_min, weight: "bold")[$0.4$] // #h(8pt) // $=>$ #text(fill: col_min, weight: "bold")[$m_(c→v) = -0.4$] // ]) // // // Étiquette "minimum" // content((-1.5, y_base + 0.4 * 0.55 + 0.95), // text(size: 0.72em, fill: col_min, style: "italic", // weight: "bold")[← minimum !]) // }) // } #let schema_min_sum_complet() = cetz.canvas(length: 1cm, { import cetz.draw: * let msgs = (2.8, 0.7, 4.2, 1.8) let sgns = (1, -1, 1, 1) let vals = ("+2.8", "-0.7", "+4.2", "+1.8") let col_ms = blue.darken(15%) let col_min = red.darken(10%) let col_vn_bg = blue.lighten(94%) // --- 1. GRAPHE DE TANNER --- group(name: "tanner", { for i in (0, 1, 2, 3) { let y_pos = 2.25 - i * 1.5 line((-6.5, y_pos), (-2.0, 0), stroke: 1.2pt + gray.lighten(30%)) } rect((-2.5, -0.5), (-1.5, 0.5), name: "cn", stroke: 2pt + orange, fill: orange.lighten(92%)) content((-2.0, 0), text(size: 1.2em, fill: orange, weight: "bold")[$c$]) for i in (0, 1, 2, 3) { let y_pos = 2.25 - i * 1.5 circle((-6.5, y_pos), radius: 0.4, name: "v" + str(i), fill: col_vn_bg, stroke: 1.5pt + blue) content("v" + str(i), text(size: 0.75em, fill: blue.darken(20%), weight: "bold")[$v_#i$]) let val = vals.at(i) content(((-6.5, y_pos), 42%, (-2.0, 0)), box( fill: col_vn_bg, stroke: 0.5pt + blue.lighten(60%), radius: 2pt, inset: 4pt, )[ #text(size: 0.65em, fill: black.lighten(10%), weight: "bold")[#val] ]) } }) // --- 2. FLÈCHE GRISE --- line((-1.0, 0), (0.5, 0), mark: (end: "stealth", fill: gray), stroke: 1.5pt + gray) // --- 3. GRAPHIQUE DES BARRES --- group(name: "bars", { let base_x = 1.4 for (i, m) in msgs.enumerate() { let x = base_x + i * 1.4 let is_min = (m == 0.7) let col = if is_min { col_min } else { col_ms } rect((x - 0.4, 0), (x + 0.4, m * 0.45), fill: col.lighten(if is_min { 75% } else { 88% }), stroke: 1.5pt + col) content((x, m * 0.45 + 0.35), text(size: 0.7em, fill: col, weight: "bold")[#m]) content((x, -0.7), box(fill: orange.lighten(92%), stroke: 0.5pt + orange, inset: 5pt, radius: 2pt)[ #text(size: 0.8em, fill: orange, weight: "bold")[#(if sgns.at(i) > 0 [+] else [−])] ]) if is_min { content((x, m * 0.45 + 0.8), text(size: 0.55em, fill: col, weight: "bold")[MIN]) } } }) // --- 4. FLÈCHE ROUGE --- line((6.5, 0), (8.0, 0), mark: (end: "stealth", fill: col_min), stroke: 2pt + col_min) // --- 5. RÉSULTAT FINAL (Décalé plus à droite à x = 11.5) --- content((10.5, 0), box(stroke: 1.5pt + col_min, fill: col_min.lighten(96%), radius: 5pt, inset: 10pt)[ #text(size: 0.8em, weight: "bold")[$m_(c arrow v_"cible") = -0.7$] ]) }) #let waterfall_plot(pts_uncoded, pts_bf) = { cetz.canvas({ import cetz.draw plot.plot( size: (23, 14.5), x-label: pad(top: 5cm)[], y-label: [BER], x-min: 0, x-max: 8, y-min: -9, y-max: 0.2, x-tick-step: 1, y-tick-step: 1, x-grid: true, y-grid: true, y-format: y => $10^(#int(y))$, x-format: x => pad(top: 1cm)[#x], legend: (2, 6), legend-style: ( padding: 0.3, stroke: gray.lighten(50%), fill: white, ), { plot.annotate({ // for decade in range(-9, 0) { // for minor in (2, 3, 4, 5, 6, 7, 8, 9) { // let y_pos = decade + (calc.log(minor) / calc.log(10)) // if y_pos >= -9 and y_pos <= 0 { // draw.line( // (0, y_pos), // (8, y_pos), // stroke: (paint: gray.lighten(70%), thickness: 0.3pt), // ) // } // } // } draw.content((4, -10), text(size: 1.2em)[$E_b/N_0$ (dB)]) }) plot.add( pts_uncoded, label: [Signal non codé], style: (stroke: (paint: orange, thickness: 2pt, dash: "dashed")), mark: "+", mark-style: (stroke: orange, size: 0.5), ) plot.add( pts_bf, label: [Bit-Flipping], style: (stroke: (paint: blue, thickness: 2.5pt)), mark: "square", mark-style: (stroke: blue, fill: white, size: 0.25), ) }, ) }) } #let convergence_plot(iters, syndrms) = { let n = iters.len() let max_s = 160.0 let W = 20.0 let H = 15.0 let bw = W / n cetz.canvas({ import cetz.draw for tick in (0, 50, 100, 150) { let y = (tick / max_s) * H draw.line((0, y), (W, y), stroke: 0.5pt + gray.lighten(60%)) draw.content((-0.8, y), text(size: 13pt)[#tick]) } for i in range(n) { let s = syndrms.at(i) let bh = (s / max_s) * H let x0 = i * bw let col = if s == 0 { green.darken(20%) } else { blue } draw.rect( (x0, 0), (x0 + bw * 0.8, bh), fill: col.lighten(70%), stroke: 1.2pt + col, ) if calc.rem(i, 2) == 0 or i == n - 1 { draw.content((x0 + bw * 0.4, -0.4), text(size: 13pt)[#i]) } } draw.line((0, 0), (W + 0.8, 0), stroke: 2pt, mark: (end: "stealth")) draw.line((0, 0), (0, H + 0.8), stroke: 2pt, mark: (end: "stealth")) draw.content((W / 2, -1), text(size: 19pt, weight: "bold")[Itérations]) draw.content((-2.2, H / 2), angle: 90deg, text(size: 19pt, weight: "bold")[Syndromes]) }) } #let waterfall_plot(pts_uncoded, pts_bf) = { cetz.canvas({ import cetz.draw: * plot.plot( size: (22, 15), x-label: none, y-label: none, x-min: 0, x-max: 10, y-min: -9, y-max: 0.2, x-tick-step: 1, y-tick-step: 1, x-grid: true, y-grid: true, style: ( axes: ( bottom: (tick: (label: (offset: 0.2))), // Chiffres plus proches de l'axe left: (tick: (label: (offset: 0.2))), ), ), y-format: y => $10^(#int(y))$, x-format: x => [#x], legend: (14.4, 12.9), legend-style: (padding: 0.3, stroke: gray.lighten(50%), fill: white), { plot.annotate({ // for decade in range(-9, 0) { // for minor in (2, 3, 4, 5, 6, 7, 8, 9) { // let y_pos = decade + (calc.log(minor) / calc.log(10)) // if y_pos >= -9 and y_pos <= 0 { // line((0, y_pos), (8, y_pos), stroke: (paint: gray.lighten(60%), thickness: 0.3pt)) // } // } // } content((4, -9.9), text(size: 1.3em)[$E_b/N_0$ (dB)]) content((-1, -4.5), angle: 90deg, text(size: 1.3em)[BER]) }) plot.add( pts_uncoded, label: [Signal non codé], style: (stroke: (paint: orange, thickness: 2pt, dash: "dashed")), mark: "+", mark-style: (stroke: orange, size: 0.5), ) plot.add( pts_bf, label: [Bit-Flipping LDPC], style: (stroke: (paint: blue, thickness: 2.5pt)), mark: "square", mark-style: (stroke: blue, fill: white, size: 0.25), ) }, ) }) } #let simulation_image_flow(img_orig, img_noisy, img_r05, img_r66, img_r75, img_w: 150pt, gap_top: 3.0, gap_bot: 4.0) = { cetz.canvas({ import cetz.draw: * let w_unit = img_w / 1cm let y_top = 5.0 let y_bot = -6.0 let x_side = (w_unit + gap_top) / 2 let x_res = w_unit + gap_bot // --- LIGNE 1 : ÉMISSION ET BRUIT (Texte collé en haut) --- content( (-x_side, y_top), [ #set align(center) #set par(leading: 0pt) #set text(bottom-edge: "baseline") // Supprime l'espace sous les lettres #text(weight: "bold", size: 1.3em)[Originale] #v(-0.6cm) #box(width: img_w, stroke: 2pt + black, radius: 4pt, inset: 0pt, img_orig) ], name: "orig", ) content( (x_side, y_top), [ #set align(center) #set par(leading: 0pt) #set text(bottom-edge: "baseline") #text(weight: "bold", size: 1.3em, fill: red)[Reçue (Bruité)] #v(-0.6cm) #box(width: img_w, stroke: 2pt + red, radius: 4pt, inset: 0pt, img_noisy) ], name: "noisy", ) // --- FLÈCHE CANAL (Ajustée pour le nouveau centre de gravité) --- let y_img_center = y_top - 1.4 line( (-x_side + w_unit / 2, y_img_center), (x_side - w_unit / 2, y_img_center), stroke: 4pt + gray, mark: (end: "stealth", fill: gray, size: 0.5), ) content((0, y_img_center + 0.8), text(size: 1.1em, fill: gray.darken(40%), style: "italic")[AWGN (2.3 dB)]) // --- LIGNE 2 : COMPARAISON DES RENDEMENTS (Texte collé en bas) --- let targets = ( (pos: (-x_res, y_bot), img: img_r05, rate: [$R = 1/2$], col: green.darken(20%), name: "r1"), (pos: (0, y_bot), img: img_r66, rate: [$R = 2/3$], col: orange, name: "r2"), (pos: (x_res, y_bot), img: img_r75, rate: [$R = 3/4$], col: red, name: "r3"), ) for t in targets { content( t.pos, [ #set align(center) #set par(leading: 0pt) #set text(top-edge: "baseline") // Supprime l'espace au-dessus des lettres #box(width: img_w, stroke: 2.5pt + t.col, radius: 4pt, inset: 0pt, t.img) #v(-0.6cm) #text(weight: "bold", size: 1.5em, fill: t.col)[#t.rate] ], name: t.name, ) } }) } #let comparison_waterfall_plot(pts_unc, pts_bf, pts_sp, pts_ms) = { cetz.canvas({ import cetz.draw: * plot.plot( size: (21, 15), x-label: none, y-label: none, x-min: 0, x-max: 11, y-min: -8, y-max: 0.2, x-tick-step: 1, y-tick-step: 1, x-grid: true, y-grid: true, style: ( axes: ( bottom: (tick: (label: (offset: 0.2))), left: (tick: (label: (offset: 0.2))), ), ), y-format: y => $10^(#int(y))$, x-format: x => [#x], legend: (13.2, 11.4), legend-style: (padding: 0.3, stroke: gray.lighten(50%), fill: white), { plot.annotate({ // Grille logarithmique étendue jusqu'à x=11 // for decade in range(-8, 0) { // for minor in (2, 3, 4, 5, 6, 7, 8, 9) { // let y_pos = decade + (calc.log(minor) / calc.log(10)) // if y_pos >= -8 and y_pos <= 0 { // line((0, y_pos), (11, y_pos), stroke: (paint: gray.lighten(75%), thickness: 0.3pt)) // } // } // } // Label X centré sur 11 unités (environ 5.5) content((5.5, -8.7), text(size: 1.3em)[$E_b/N_0$ (dB)]) // Label Y content((-1.2, -4.0), angle: 90deg, text(size: 1.3em)[BER]) }) plot.add( pts_unc, label: [Signal non codé], style: (stroke: (paint: orange, thickness: 2pt, dash: "dashed")), mark: "+", mark-style: (stroke: orange, size: 1), ) plot.add( pts_bf, label: [Bit-Flipping (Hard)], style: (stroke: (paint: red, thickness: 2pt)), mark: "x", mark-style: (stroke: red, size: 1), ) plot.add( pts_ms, label: [Min-Sum (Soft)], style: (stroke: (paint: green.darken(20%), thickness: 2pt)), mark: "triangle", mark-style: (stroke: green.darken(20%), fill: white, size: 0.3), ) plot.add( pts_sp, label: [Sum-Product (Soft)], style: (stroke: (paint: blue, thickness: 2pt)), mark: "square", mark-style: (stroke: blue, fill: white, size: 1), ) }, ) }) }