2884 lines
79 KiB
Typst
2884 lines
79 KiB
Typst
#import "@preview/polylux:0.4.0": *
|
||
#import "@preview/cetz:0.5.0"
|
||
#import "@preview/cetz-plot:0.1.3": plot
|
||
|
||
#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(page).get().first()
|
||
// let tot = counter(page).final().first()
|
||
let cur = counter("logical-slide").get().first()
|
||
let tot = 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], [#(cur - 1) / #(tot - 1)],
|
||
)
|
||
]
|
||
}
|
||
}
|
||
]
|
||
|
||
// 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 = blue
|
||
let color_orange = orange
|
||
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$]
|
||
])
|
||
})
|