Files
Presentation-TIPE/composants.typ
2026-05-13 09:20:12 +02:00

3344 lines
90 KiB
Typst
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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(<fin>)
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 #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),
)
let darken_ration = 20%
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").darken(darken_ration))
}
for p in vpos {
circle((p.at(0), p.at(1)), radius: 0.25, fill: white, stroke: 0.8pt + rgb("#cccccc").darken(darken_ration))
}
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").darken(darken_ration),
)
}
})
}
// 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)
}
})
}
#let hldpc_triple(row1: 0, row2: 9, row3: 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), // Ligne 9 contient 24
(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), // Ligne 14 contient 24
)
cetz.canvas(length: 0.35cm, {
let nx = 30
let ny = 15
let col_1 = orange
let col_2 = green.darken(15%)
let col_3 = blue
draw.content((-3.4, -7.5), text(size: 1.6em)[$H =$])
// Fonds de lignes (Highlight)
draw.rect((0, -row1), (nx, -row1 - 1), fill: col_1.lighten(90%), stroke: none)
draw.rect((0, -row2), (nx, -row2 - 1), fill: col_2.lighten(90%), stroke: none)
draw.rect((0, -row3), (nx, -row3 - 1), fill: col_3.lighten(90%), stroke: none)
// Crochets
draw.set-style(stroke: (thickness: 1.2pt))
draw.line((0.5, 0.3), (0, 0.3), (0, -ny - 0.3), (0.5, -ny - 0.3))
draw.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
} else if y == row3 {
d_col = col_3
r = 0.22
}
draw.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),
)
},
)
})
}