File size: 5,708 Bytes
06e4b6f
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
//! `jepa::mascaras` — extraído do monólito na v0.10.0
//! (divisão SRP, crítica Qwen3.8 lote 3 nº 1; r-35 §2).

use super::*;


// ---------------------------------------------------------------------------
// Parte 2 — máscaras
// ---------------------------------------------------------------------------

/// Máscara de blocos 2D (Image-JEPA): grade (h, w) de patches; sorteia
/// `n_blocos` retângulos aleatórios com fração alvo `frac` de patches.
pub fn mascara_blocos_2d(h: usize, w: usize, n_blocos: usize, frac: f64, rng: &mut RngDeterministico) -> Vec<bool> {
    let total = h * w;
    let alvo = (total as f64 * frac) as usize;
    let mut masc = vec![false; total];
    let mut coberto = 0usize;
    for _ in 0..n_blocos.max(1) {
        if coberto >= alvo {
            break;
        }
        let bh = 1 + rng.inteiro(h.min(4));
        let bw = 1 + rng.inteiro(w.min(4));
        let y0 = rng.inteiro(h + 1 - bh);
        let x0 = rng.inteiro(w + 1 - bw);
        for y in y0..y0 + bh {
            for x in x0..x0 + bw {
                let i = y * w + x;
                if !masc[i] {
                    masc[i] = true;
                    coberto += 1;
                }
            }
        }
    }
    masc
}

/// Máscara de tubos 3D (V-JEPA 2): grade (t, h, w); tubos = retângulos que
/// atravessam o tempo.
pub fn mascara_tubos_3d(t: usize, h: usize, w: usize, n_tubos: usize, frac: f64, rng: &mut RngDeterministico) -> Vec<bool> {
    let total = t * h * w;
    let alvo = (total as f64 * frac) as usize;
    let mut masc = vec![false; total];
    let mut coberto = 0usize;
    for _ in 0..n_tubos.max(1) {
        if coberto >= alvo {
            break;
        }
        let bh = 1 + rng.inteiro(h.min(3));
        let bw = 1 + rng.inteiro(w.min(3));
        let y0 = rng.inteiro(h + 1 - bh);
        let x0 = rng.inteiro(w + 1 - bw);
        for tt in 0..t {
            for y in y0..y0 + bh {
                for x in x0..x0 + bw {
                    let i = (tt * h + y) * w + x;
                    if !masc[i] {
                        masc[i] = true;
                        coberto += 1;
                    }
                }
            }
        }
    }
    masc
}

/// Máscara de bandas (Audio-JEPA): grade (f, tau) — bandas de frequência
/// horizontais com extensão temporal.
pub fn mascara_bandas(f: usize, tau: usize, n_bandas: usize, frac: f64, rng: &mut RngDeterministico) -> Vec<bool> {
    mascara_blocos_2d(f, tau, n_bandas, frac, rng) // blocos na grade (f, tau)
}

/// Máscara de blocos 3D (3D-JEPA): grade (x, y, z) de voxels.
pub fn mascara_blocos_3d(x: usize, y: usize, z: usize, n_blocos: usize, frac: f64, rng: &mut RngDeterministico) -> Vec<bool> {
    mascara_tubos_3d(x, y, z, n_blocos, frac, rng)
}

/// Máscara de segmentos canal×tempo (Brain-JEPA): canais inteiros OU
/// janelas temporais (aleatório por chamada).
pub fn mascara_segmentos(c: usize, t: usize, frac: f64, rng: &mut RngDeterministico) -> Vec<bool> {
    let total = c * t;
    let alvo = (total as f64 * frac) as usize;
    let mut masc = vec![false; total];
    if rng.inteiro(2) == 0 {
        // canais inteiros
        let mut coberto = 0;
        let mut canais: Vec<usize> = (0..c).collect();
        // embaralhar
        for i in (1..canais.len()).rev() {
            let j = rng.inteiro(i + 1);
            canais.swap(i, j);
        }
        for &ch in canais.iter() {
            if coberto >= alvo {
                break;
            }
            for tt in 0..t {
                masc[ch * t + tt] = true;
                coberto += 1;
            }
        }
    } else {
        // janelas temporais
        let mut coberto = 0;
        let mut t0 = 0;
        while coberto < alvo && t0 < t {
            let len = 1 + rng.inteiro((t / 4).max(1));
            for ch in 0..c {
                for tt in t0..(t0 + len).min(t) {
                    masc[ch * t + tt] = true;
                    coberto += 1;
                }
            }
            t0 += len + 1;
        }
    }
    masc
}

/// Máscara 1D (T-JEPA): intervalos contíguos em n posições.
pub fn mascara_intervalos_1d(n: usize, frac: f64, rng: &mut RngDeterministico) -> Vec<bool> {
    let alvo = (n as f64 * frac) as usize;
    let mut masc = vec![false; n];
    let mut coberto = 0;
    while coberto < alvo {
        let len = 1 + rng.inteiro((n / 8).max(1));
        let i0 = rng.inteiro(n);
        for i in i0..(i0 + len).min(n) {
            if !masc[i] {
                masc[i] = true;
                coberto += 1;
            }
        }
    }
    masc
}

/// Amostragem dos pontos mais distantes (FPS) para Point-JEPA.
pub fn pontos_mais_distantes(pontos: &[Vec<f64>], k: usize, rng: &mut RngDeterministico) -> Vec<usize> {
    let n = pontos.len();
    if n == 0 || k == 0 {
        return vec![];
    }
    let k = k.min(n);
    let mut centros = vec![rng.inteiro(n)];
    let mut dist_min: Vec<f64> = pontos
        .iter()
        .map(|p| distancia_eucl(p, &pontos[centros[0]]))
        .collect();
    while centros.len() < k {
        let prox = dist_min
            .iter()
            .enumerate()
            .filter(|(i, _)| !centros.contains(i))
            .max_by(|a, b| a.1.partial_cmp(b.1).unwrap())
            .map(|(i, _)| i)
            .unwrap_or(0);
        centros.push(prox);
        for (i, p) in pontos.iter().enumerate() {
            let d = distancia_eucl(p, &pontos[prox]);
            if d < dist_min[i] {
                dist_min[i] = d;
            }
        }
    }
    centros
}

pub(crate) fn distancia_eucl(a: &[f64], b: &[f64]) -> f64 {
    a.iter().zip(b).map(|(x, y)| (x - y) * (x - y)).sum::<f64>().sqrt()
}