1use scirs2_core::random::quick::random_f32;
5use scirs2_core::slice_random::shuffle;
6use torsh_core::error::{Result, TorshError};
7use torsh_tensor::{creation::*, Tensor};
8
9pub trait Initializer {
11 fn initialize(&self, shape: &[usize]) -> Result<Tensor>;
13}
14
15#[derive(Debug, Clone)]
17pub enum InitMethod {
18 XavierUniform { gain: f32 },
20 XavierNormal { gain: f32 },
22 KaimingUniform {
24 mode: FanMode,
25 nonlinearity: Nonlinearity,
26 },
27 KaimingNormal {
29 mode: FanMode,
30 nonlinearity: Nonlinearity,
31 },
32 Uniform { low: f32, high: f32 },
34 Normal { mean: f32, std: f32 },
36 Zeros,
38 Ones,
40 Constant { value: f32 },
42 Orthogonal { gain: f32 },
44 Sparse { sparsity: f32, std: f32 },
46 Eye,
48 LecunUniform,
50 LecunNormal,
52 TruncatedNormal { mean: f32, std: f32, a: f32, b: f32 },
54 VarianceScaling {
56 scale: f32,
57 mode: FanMode,
58 distribution: Distribution,
59 },
60 Dirac,
62 SIREN { c: f32, w0: f32 },
65}
66
67#[derive(Debug, Clone, Copy)]
69pub enum Distribution {
70 Uniform,
72 Normal,
74 TruncatedNormal,
76}
77
78#[derive(Debug, Clone, Copy)]
80pub enum FanMode {
81 FanIn,
82 FanOut,
83 FanAvg,
84}
85
86#[derive(Debug, Clone, Copy)]
88pub enum Nonlinearity {
89 ReLU,
90 LeakyReLU { negative_slope: f32 },
91 Tanh,
92 Sigmoid,
93 SELU,
94 ELU,
95 Swish,
96 Linear,
97}
98
99impl Nonlinearity {
100 pub fn gain(&self) -> f32 {
107 match self {
108 Nonlinearity::ReLU => (2.0_f32).sqrt(),
109 Nonlinearity::LeakyReLU { negative_slope } => {
110 (2.0 / (1.0 + negative_slope.powi(2))).sqrt()
111 }
112 Nonlinearity::Tanh => (5.0_f32 / 3.0_f32).sqrt(),
113 Nonlinearity::Sigmoid => 1.0,
114 Nonlinearity::SELU => 3.0_f32 / 4.0_f32,
116 Nonlinearity::ELU => 1.0,
119 Nonlinearity::Swish => (2.0_f32).sqrt(),
121 Nonlinearity::Linear => 1.0,
122 }
123 }
124}
125
126impl InitMethod {
127 pub fn xavier_uniform() -> Self {
129 InitMethod::XavierUniform { gain: 1.0 }
130 }
131
132 pub fn xavier_normal() -> Self {
134 InitMethod::XavierNormal { gain: 1.0 }
135 }
136
137 pub fn kaiming_uniform() -> Self {
139 InitMethod::KaimingUniform {
140 mode: FanMode::FanIn,
141 nonlinearity: Nonlinearity::ReLU,
142 }
143 }
144
145 pub fn kaiming_normal() -> Self {
147 InitMethod::KaimingNormal {
148 mode: FanMode::FanIn,
149 nonlinearity: Nonlinearity::ReLU,
150 }
151 }
152
153 pub fn uniform_range(low: f32, high: f32) -> Self {
155 InitMethod::Uniform { low, high }
156 }
157
158 pub fn normal_dist(mean: f32, std: f32) -> Self {
160 InitMethod::Normal { mean, std }
161 }
162
163 pub fn zeros() -> Self {
165 InitMethod::Zeros
166 }
167
168 pub fn ones() -> Self {
170 InitMethod::Ones
171 }
172
173 pub fn constant(value: f32) -> Self {
175 InitMethod::Constant { value }
176 }
177
178 pub fn orthogonal() -> Self {
180 InitMethod::Orthogonal { gain: 1.0 }
181 }
182
183 pub fn lecun_uniform() -> Self {
185 InitMethod::LecunUniform
186 }
187
188 pub fn lecun_normal() -> Self {
190 InitMethod::LecunNormal
191 }
192
193 pub fn dirac() -> Self {
195 InitMethod::Dirac
196 }
197
198 pub fn siren_first_layer() -> Self {
200 InitMethod::SIREN { c: 1.0, w0: 30.0 }
201 }
202
203 pub fn siren_hidden_layer() -> Self {
205 InitMethod::SIREN { c: 6.0, w0: 1.0 }
206 }
207
208 pub fn with_gain(self, gain: f32) -> Self {
210 match self {
211 InitMethod::XavierUniform { .. } => InitMethod::XavierUniform { gain },
212 InitMethod::XavierNormal { .. } => InitMethod::XavierNormal { gain },
213 InitMethod::Orthogonal { .. } => InitMethod::Orthogonal { gain },
214 other => other,
215 }
216 }
217
218 pub fn with_fan_mode(self, mode: FanMode) -> Self {
220 match self {
221 InitMethod::KaimingUniform {
222 nonlinearity,
223 mode: _,
224 } => InitMethod::KaimingUniform { mode, nonlinearity },
225 InitMethod::KaimingNormal {
226 nonlinearity,
227 mode: _,
228 } => InitMethod::KaimingNormal { mode, nonlinearity },
229 InitMethod::VarianceScaling {
230 scale,
231 distribution,
232 mode: _,
233 } => InitMethod::VarianceScaling {
234 scale,
235 mode,
236 distribution,
237 },
238 other => other,
239 }
240 }
241
242 pub fn with_nonlinearity(self, nonlinearity: Nonlinearity) -> Self {
244 match self {
245 InitMethod::KaimingUniform { mode, .. } => {
246 InitMethod::KaimingUniform { mode, nonlinearity }
247 }
248 InitMethod::KaimingNormal { mode, .. } => {
249 InitMethod::KaimingNormal { mode, nonlinearity }
250 }
251 other => other,
252 }
253 }
254
255 pub fn name(&self) -> &str {
257 match self {
258 InitMethod::XavierUniform { .. } => "Xavier Uniform",
259 InitMethod::XavierNormal { .. } => "Xavier Normal",
260 InitMethod::KaimingUniform { .. } => "Kaiming Uniform",
261 InitMethod::KaimingNormal { .. } => "Kaiming Normal",
262 InitMethod::Uniform { .. } => "Uniform",
263 InitMethod::Normal { .. } => "Normal",
264 InitMethod::Zeros => "Zeros",
265 InitMethod::Ones => "Ones",
266 InitMethod::Constant { .. } => "Constant",
267 InitMethod::Orthogonal { .. } => "Orthogonal",
268 InitMethod::Sparse { .. } => "Sparse",
269 InitMethod::Eye => "Eye/Identity",
270 InitMethod::LecunUniform => "LeCun Uniform",
271 InitMethod::LecunNormal => "LeCun Normal",
272 InitMethod::TruncatedNormal { .. } => "Truncated Normal",
273 InitMethod::VarianceScaling { .. } => "Variance Scaling",
274 InitMethod::Dirac => "Dirac",
275 InitMethod::SIREN { .. } => "SIREN",
276 }
277 }
278}
279
280impl Initializer for InitMethod {
281 fn initialize(&self, shape: &[usize]) -> Result<Tensor> {
282 match self {
283 InitMethod::XavierUniform { gain } => xavier_uniform_with_gain(shape, *gain),
284 InitMethod::XavierNormal { gain } => xavier_normal_with_gain(shape, *gain),
285 InitMethod::KaimingUniform { mode, nonlinearity } => {
286 kaiming_uniform_with_nonlinearity(shape, *mode, *nonlinearity)
287 }
288 InitMethod::KaimingNormal { mode, nonlinearity } => {
289 kaiming_normal_with_nonlinearity(shape, *mode, *nonlinearity)
290 }
291 InitMethod::Uniform { low, high } => uniform(shape, *low, *high),
292 InitMethod::Normal { mean, std } => normal(shape, *mean, *std),
293 InitMethod::Zeros => zeros(shape),
294 InitMethod::Ones => ones(shape),
295 InitMethod::Constant { value } => constant(shape, *value),
296 InitMethod::Orthogonal { gain } => orthogonal_init(shape, *gain),
297 InitMethod::Sparse { sparsity, std } => sparse_init(shape, *sparsity, *std),
298 InitMethod::Eye => eye_init_tensor(shape),
299 InitMethod::LecunUniform => lecun_uniform(shape),
300 InitMethod::LecunNormal => lecun_normal(shape),
301 InitMethod::TruncatedNormal { mean, std, a, b } => {
302 truncated_normal(shape, *mean, *std, *a, *b)
303 }
304 InitMethod::VarianceScaling {
305 scale,
306 mode,
307 distribution,
308 } => variance_scaling(shape, *scale, *mode, *distribution),
309 InitMethod::Dirac => dirac_init(shape),
310 InitMethod::SIREN { c, w0 } => siren_init(shape, *c, *w0),
311 }
312 }
313}
314
315pub fn constant(shape: &[usize], value: f32) -> Result<Tensor> {
317 let size = shape.iter().product();
318 let values = vec![value; size];
319 Tensor::from_vec(values, shape)
320 .map_err(|e| TorshError::RuntimeError(format!("Failed to create constant tensor: {}", e)))
321}
322
323pub fn init(method: InitMethod) -> impl Initializer {
325 method
326}
327
328pub fn calculate_fan_in_fan_out(shape: &[usize]) -> Result<(usize, usize)> {
330 let dimensions = shape.len();
331
332 if dimensions < 2 {
333 return Err(TorshError::InvalidArgument(
334 "Fan in and fan out can not be computed for tensor with fewer than 2 dimensions"
335 .to_string(),
336 ));
337 }
338
339 let num_input_fmaps = shape[1];
340 let num_output_fmaps = shape[0];
341
342 let mut receptive_field_size = 1;
343 if dimensions > 2 {
344 for &size in shape.iter().skip(2).take(dimensions - 2) {
345 receptive_field_size *= size;
346 }
347 }
348
349 let fan_in = num_input_fmaps * receptive_field_size;
350 let fan_out = num_output_fmaps * receptive_field_size;
351
352 Ok((fan_in, fan_out))
353}
354
355pub fn calculate_fan(shape: &[usize], mode: FanMode) -> Result<usize> {
357 let (fan_in, fan_out) = calculate_fan_in_fan_out(shape)?;
358
359 match mode {
360 FanMode::FanIn => Ok(fan_in),
361 FanMode::FanOut => Ok(fan_out),
362 FanMode::FanAvg => Ok((fan_in + fan_out) / 2),
363 }
364}
365
366pub fn xavier_uniform(shape: &[usize]) -> Result<Tensor> {
368 xavier_uniform_with_gain(shape, 1.0)
369}
370
371pub fn xavier_uniform_with_gain(shape: &[usize], gain: f32) -> Result<Tensor> {
373 let (fan_in, fan_out) = calculate_fan_in_fan_out(shape)?;
374 let std = gain * (2.0 / (fan_in + fan_out) as f32).sqrt();
375 let bound = std * 3.0_f32.sqrt();
376
377 uniform(shape, -bound, bound)
378}
379
380pub fn xavier_normal(shape: &[usize]) -> Result<Tensor> {
382 xavier_normal_with_gain(shape, 1.0)
383}
384
385pub fn xavier_normal_with_gain(shape: &[usize], gain: f32) -> Result<Tensor> {
387 let (fan_in, fan_out) = calculate_fan_in_fan_out(shape)?;
388 let std = gain * (2.0 / (fan_in + fan_out) as f32).sqrt();
389
390 normal(shape, 0.0, std)
391}
392
393pub fn kaiming_uniform(shape: &[usize], mode: &str) -> Result<Tensor> {
395 let fan_mode = match mode {
396 "fan_in" => FanMode::FanIn,
397 "fan_out" => FanMode::FanOut,
398 "fan_avg" => FanMode::FanAvg,
399 _ => {
400 return Err(TorshError::InvalidArgument(format!(
401 "Mode {} not supported, please use one of 'fan_in', 'fan_out', or 'fan_avg'.",
402 mode
403 )))
404 }
405 };
406
407 kaiming_uniform_with_nonlinearity(shape, fan_mode, Nonlinearity::ReLU)
408}
409
410pub fn kaiming_uniform_with_nonlinearity(
412 shape: &[usize],
413 mode: FanMode,
414 nonlinearity: Nonlinearity,
415) -> Result<Tensor> {
416 let fan = calculate_fan(shape, mode)?;
417 let gain = nonlinearity.gain();
418 let std = gain / (fan as f32).sqrt();
419 let bound = std * 3.0_f32.sqrt();
420
421 uniform(shape, -bound, bound)
422}
423
424pub fn kaiming_normal(shape: &[usize], mode: &str) -> Result<Tensor> {
426 let fan_mode = match mode {
427 "fan_in" => FanMode::FanIn,
428 "fan_out" => FanMode::FanOut,
429 "fan_avg" => FanMode::FanAvg,
430 _ => {
431 return Err(TorshError::InvalidArgument(format!(
432 "Mode {} not supported, please use one of 'fan_in', 'fan_out', or 'fan_avg'.",
433 mode
434 )))
435 }
436 };
437
438 kaiming_normal_with_nonlinearity(shape, fan_mode, Nonlinearity::ReLU)
439}
440
441pub fn kaiming_normal_with_nonlinearity(
443 shape: &[usize],
444 mode: FanMode,
445 nonlinearity: Nonlinearity,
446) -> Result<Tensor> {
447 let fan = calculate_fan(shape, mode)?;
448 let gain = nonlinearity.gain();
449 let std = gain / (fan as f32).sqrt();
450
451 normal(shape, 0.0, std)
452}
453
454pub fn uniform(shape: &[usize], low: f32, high: f32) -> Result<Tensor> {
456 if low >= high {
457 return Err(TorshError::InvalidArgument(
458 "Low bound must be less than high bound for uniform initialization".to_string(),
459 ));
460 }
461
462 let size = shape.iter().product();
463 let range = high - low;
464 let values: Vec<f32> = (0..size).map(|_| low + random_f32() * range).collect();
465
466 Tensor::from_vec(values, shape)
467 .map_err(|e| TorshError::RuntimeError(format!("Failed to create uniform tensor: {}", e)))
468}
469
470fn box_muller(u1: f32, u2: f32) -> f32 {
478 let u1 = u1.max(f32::MIN_POSITIVE);
479 (-2.0 * u1.ln()).sqrt() * (2.0 * std::f32::consts::PI * u2).cos()
480}
481
482fn standard_normal() -> f32 {
484 box_muller(random_f32(), random_f32())
485}
486
487pub fn normal(shape: &[usize], mean: f32, std: f32) -> Result<Tensor> {
489 if std <= 0.0 {
490 return Err(TorshError::InvalidArgument(
491 "Standard deviation must be positive for normal initialization".to_string(),
492 ));
493 }
494
495 let size = shape.iter().product();
496 let values: Vec<f32> = (0..size).map(|_| mean + standard_normal() * std).collect();
497
498 Tensor::from_vec(values, shape)
499 .map_err(|e| TorshError::RuntimeError(format!("Failed to create normal tensor: {}", e)))
500}
501
502pub fn lecun_uniform(shape: &[usize]) -> Result<Tensor> {
504 let fan_in = calculate_fan(shape, FanMode::FanIn)?;
505 let limit = (3.0 / fan_in as f32).sqrt();
506 uniform(shape, -limit, limit)
507}
508
509pub fn lecun_normal(shape: &[usize]) -> Result<Tensor> {
511 let fan_in = calculate_fan(shape, FanMode::FanIn)?;
512 let std = (1.0 / fan_in as f32).sqrt();
513 normal(shape, 0.0, std)
514}
515
516pub fn truncated_normal(shape: &[usize], mean: f32, std: f32, a: f32, b: f32) -> Result<Tensor> {
518 if std <= 0.0 {
519 return Err(TorshError::InvalidArgument(
520 "Standard deviation must be positive for truncated normal initialization".to_string(),
521 ));
522 }
523
524 if a >= b {
525 return Err(TorshError::InvalidArgument(
526 "Lower bound must be less than upper bound for truncated normal initialization"
527 .to_string(),
528 ));
529 }
530
531 let size = shape.iter().product();
532 let mut values = Vec::with_capacity(size);
533
534 for _ in 0..size {
535 loop {
536 let sample = mean + standard_normal() * std;
537 if sample >= a && sample <= b {
538 values.push(sample);
539 break;
540 }
541 }
542 }
543
544 Tensor::from_vec(values, shape).map_err(|e| {
545 TorshError::RuntimeError(format!("Failed to create truncated normal tensor: {}", e))
546 })
547}
548
549pub fn eye_init(n: usize) -> Result<Tensor> {
551 eye(n).map_err(|e| TorshError::RuntimeError(format!("Failed to create eye tensor: {}", e)))
552}
553
554pub fn eye_init_tensor(shape: &[usize]) -> Result<Tensor> {
556 if shape.len() < 2 {
557 return Err(TorshError::InvalidArgument(
558 "Eye initialization requires at least 2D tensor".to_string(),
559 ));
560 }
561
562 let rows = shape[0];
563 let cols = shape[1];
564
565 if rows != cols {
566 return Err(TorshError::InvalidArgument(
567 "Eye initialization requires square matrices (rows == cols)".to_string(),
568 ));
569 }
570
571 eye_init(rows)
572}
573
574pub fn orthogonal_init(shape: &[usize], gain: f32) -> Result<Tensor> {
594 if shape.len() < 2 {
595 return Err(TorshError::InvalidArgument(
596 "Orthogonal initialization requires at least 2D tensor".to_string(),
597 ));
598 }
599
600 let num_rows = shape[0];
601 let num_cols: usize = shape[1..].iter().product();
602
603 if num_rows == 0 || num_cols == 0 {
604 return Err(TorshError::InvalidArgument(format!(
605 "Orthogonal initialization requires a non-empty shape, got {shape:?}"
606 )));
607 }
608
609 let transposed = num_rows < num_cols;
612 let (qr_rows, qr_cols) = if transposed {
613 (num_cols, num_rows)
614 } else {
615 (num_rows, num_cols)
616 };
617
618 let random_tensor = normal(&[qr_rows, qr_cols], 0.0, 1.0)?;
619 let (q, r) = torsh_linalg::decomposition::qr(&random_tensor)?;
620
621 let mut signs = Vec::with_capacity(qr_cols);
625 for j in 0..qr_cols {
626 let diagonal = r.get(&[j, j])?;
627 signs.push(if diagonal < 0.0 { -1.0f32 } else { 1.0f32 });
628 }
629
630 let mut values = vec![0.0f32; num_rows * num_cols];
632 for row in 0..num_rows {
633 for col in 0..num_cols {
634 let (q_row, q_col) = if transposed { (col, row) } else { (row, col) };
637 values[row * num_cols + col] = q.get(&[q_row, q_col])? * signs[q_col] * gain;
638 }
639 }
640
641 Tensor::from_vec(values, shape)
642 .map_err(|e| TorshError::RuntimeError(format!("Failed to create orthogonal tensor: {e}")))
643}
644
645pub fn sparse_init(shape: &[usize], sparsity: f32, std: f32) -> Result<Tensor> {
647 if shape.len() != 2 {
648 return Err(TorshError::InvalidArgument(
649 "Only tensors with 2 dimensions are supported for sparse initialization".to_string(),
650 ));
651 }
652
653 if !(0.0..=1.0).contains(&sparsity) {
654 return Err(TorshError::InvalidArgument(
655 "Sparsity must be between 0.0 and 1.0".to_string(),
656 ));
657 }
658
659 let rows = shape[0];
660 let cols = shape[1];
661 let total_elements = rows * cols;
662 let num_zeros = (total_elements as f32 * sparsity) as usize;
663
664 let mut values = Vec::with_capacity(total_elements);
666
667 for _ in 0..total_elements {
668 values.push(standard_normal() * std); }
670
671 let mut indices: Vec<usize> = (0..total_elements).collect();
674 shuffle(&mut indices);
676
677 for &idx in indices.iter().take(num_zeros) {
678 values[idx] = 0.0;
679 }
680
681 Tensor::from_vec(values, shape)
682 .map_err(|e| TorshError::RuntimeError(format!("Failed to create sparse tensor: {}", e)))
683}
684
685pub fn init_tensor(
687 tensor: &mut Tensor,
688 method: &str,
689 gain: Option<f32>,
690 mode: Option<&str>,
691) -> Result<()> {
692 let binding = tensor.shape();
693 let shape = binding.dims();
694 let gain = gain.unwrap_or(1.0);
695 let mode = mode.unwrap_or("fan_in");
696
697 let initialized = match method {
698 "xavier_uniform" | "glorot_uniform" => xavier_uniform_with_gain(shape, gain),
699 "xavier_normal" | "glorot_normal" => xavier_normal_with_gain(shape, gain),
700 "kaiming_uniform" | "he_uniform" => kaiming_uniform(shape, mode),
701 "kaiming_normal" | "he_normal" => kaiming_normal(shape, mode),
702 "orthogonal" => orthogonal_init(shape, gain),
703 "lecun_uniform" => lecun_uniform(shape),
704 "lecun_normal" => lecun_normal(shape),
705 "zeros" => zeros(shape),
706 "ones" => ones(shape),
707 "eye" => eye_init_tensor(shape),
708 _ => {
709 return Err(TorshError::InvalidArgument(format!(
710 "Unknown initialization method: {}",
711 method
712 )))
713 }
714 }?;
715
716 *tensor = initialized;
717 Ok(())
718}
719
720pub trait Initializable {
722 fn reset_parameters(&mut self);
723}
724
725pub fn variance_scaling(
740 shape: &[usize],
741 scale: f32,
742 mode: FanMode,
743 distribution: Distribution,
744) -> Result<Tensor> {
745 let fan = calculate_fan(shape, mode)?;
746 let variance = scale / fan as f32;
747
748 match distribution {
749 Distribution::Uniform => {
750 let limit = (3.0 * variance).sqrt();
751 uniform(shape, -limit, limit)
752 }
753 Distribution::Normal => {
754 let std = variance.sqrt();
755 normal(shape, 0.0, std)
756 }
757 Distribution::TruncatedNormal => {
758 let std = variance.sqrt();
759 truncated_normal(shape, 0.0, std, -2.0 * std, 2.0 * std)
761 }
762 }
763}
764
765pub fn dirac_init(shape: &[usize]) -> Result<Tensor> {
781 if shape.len() < 3 {
782 return Err(TorshError::InvalidArgument(
783 "Dirac initialization requires at least 3D tensor (out_channels, in_channels, kernel_size)".to_string(),
784 ));
785 }
786
787 let out_channels = shape[0];
788 let in_channels = shape[1];
789
790 let total_size: usize = shape.iter().product();
792 let kernel_spatial_size: usize = shape[2..].iter().product();
793
794 let mut values = vec![0.0_f32; total_size];
796
797 let mut center_offset = 0;
799 let mut stride = 1;
800 for &dim_size in shape[2..].iter().rev() {
801 center_offset += (dim_size / 2) * stride;
802 stride *= dim_size;
803 }
804
805 let min_channels = out_channels.min(in_channels);
807 for i in 0..min_channels {
808 let idx = i * in_channels * kernel_spatial_size + i * kernel_spatial_size + center_offset;
809 if idx < total_size {
810 values[idx] = 1.0;
811 }
812 }
813
814 Tensor::from_vec(values, shape)
815 .map_err(|e| TorshError::RuntimeError(format!("Failed to create Dirac tensor: {}", e)))
816}
817
818pub fn siren_init(shape: &[usize], c: f32, w0: f32) -> Result<Tensor> {
835 if shape.len() < 2 {
836 return Err(TorshError::InvalidArgument(
837 "SIREN initialization requires at least 2D tensor".to_string(),
838 ));
839 }
840
841 let fan_in = calculate_fan(shape, FanMode::FanIn)?;
842
843 let bound = if (w0 - 1.0).abs() < 1e-6 {
846 (c / fan_in as f32).sqrt()
848 } else {
849 1.0 / fan_in as f32
851 };
852
853 let mut tensor = uniform(shape, -bound, bound)?;
854
855 if (w0 - 1.0).abs() > 1e-6 {
857 let values: Vec<f32> = tensor.to_vec()?.iter().map(|&v| v * w0).collect();
858 tensor = Tensor::from_vec(values, shape)?;
859 }
860
861 Ok(tensor)
862}
863
864pub fn fixup_init(
886 shape: &[usize],
887 num_layers: usize,
888 num_residual_blocks: usize,
889 is_residual_branch: bool,
890) -> Result<Tensor> {
891 let mut tensor = kaiming_normal_with_nonlinearity(shape, FanMode::FanIn, Nonlinearity::ReLU)?;
893
894 if is_residual_branch && num_residual_blocks > 1 {
895 let exponent = -1.0 / (2.0 * num_residual_blocks as f32 - 2.0);
897 let scale = (2.0 * num_layers as f32).powf(exponent);
898
899 let values: Vec<f32> = tensor.to_vec()?.iter().map(|&v| v * scale).collect();
901 tensor = Tensor::from_vec(values, shape).map_err(|e| {
902 TorshError::RuntimeError(format!("Failed to create Fixup tensor: {}", e))
903 })?;
904 }
905
906 Ok(tensor)
907}
908
909pub fn rezero_init(shape: &[usize]) -> Result<Tensor> {
925 kaiming_normal_with_nonlinearity(shape, FanMode::FanIn, Nonlinearity::ReLU)
928}
929
930pub fn rezero_alpha_init() -> Result<Tensor> {
932 Tensor::from_vec(vec![0.0_f32], &[1])
933 .map_err(|e| TorshError::RuntimeError(format!("Failed to create ReZero alpha: {}", e)))
934}
935
936pub fn delta_orthogonal_init(shape: &[usize], gain: f32) -> Result<Tensor> {
948 if shape.len() < 2 {
949 return Err(TorshError::InvalidArgument(
950 "Delta-Orthogonal initialization requires at least 2D tensor".to_string(),
951 ));
952 }
953
954 orthogonal_init(shape, gain)
957}
958
959pub fn metainit(shape: &[usize], sparsity: f32, scale: f32) -> Result<Tensor> {
973 if sparsity < 0.0 || sparsity >= 1.0 {
974 return Err(TorshError::InvalidArgument(format!(
975 "Sparsity must be in [0, 1), got {}",
976 sparsity
977 )));
978 }
979
980 if scale <= 0.0 {
981 return Err(TorshError::InvalidArgument(format!(
982 "Scale must be positive, got {}",
983 scale
984 )));
985 }
986
987 let size = shape.iter().product();
988 let mut values = Vec::with_capacity(size);
989
990 for _ in 0..size {
991 if random_f32() < sparsity {
992 values.push(0.0);
993 } else {
994 let sign = if random_f32() < 0.5 { -1.0 } else { 1.0 };
996 values.push(sign * scale * random_f32());
997 }
998 }
999
1000 Tensor::from_vec(values, shape)
1001 .map_err(|e| TorshError::RuntimeError(format!("Failed to create MetaInit tensor: {}", e)))
1002}
1003
1004pub fn lsuv_init(shape: &[usize]) -> Result<Tensor> {
1020 orthogonal_init(shape, 1.0)
1023}
1024
1025pub fn zero_centered_variance_init(shape: &[usize], target_variance: f32) -> Result<Tensor> {
1038 if target_variance <= 0.0 {
1039 return Err(TorshError::InvalidArgument(format!(
1040 "Target variance must be positive, got {}",
1041 target_variance
1042 )));
1043 }
1044
1045 let std = target_variance.sqrt();
1046 normal(shape, 0.0, std)
1047}
1048
1049pub fn gan_balanced_init(shape: &[usize], is_generator: bool) -> Result<Tensor> {
1062 let gain = if is_generator { 0.5 } else { 1.0 };
1063
1064 let fan_in = calculate_fan(shape, FanMode::FanIn)?;
1065 let fan_out = calculate_fan(shape, FanMode::FanOut)?;
1066 let fan_avg = (fan_in + fan_out) / 2;
1067
1068 let std = gain * (2.0 / fan_avg as f32).sqrt();
1069 normal(shape, 0.0, std)
1070}
1071
1072pub fn coordinate_mlp_init(shape: &[usize], omega_0: f32) -> Result<Tensor> {
1084 if shape.len() < 2 {
1085 return Err(TorshError::InvalidArgument(
1086 "Coordinate MLP initialization requires at least 2D tensor".to_string(),
1087 ));
1088 }
1089
1090 let fan_in = calculate_fan(shape, FanMode::FanIn)?;
1091 let std = 1.0 / (fan_in as f32 * omega_0).sqrt();
1092
1093 normal(shape, 0.0, std)
1094}
1095
1096#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1102pub enum ArchitectureHint {
1103 Feedforward,
1105 Convolutional,
1107 Recurrent,
1109 Transformer,
1111 Residual,
1113 VeryDeep,
1115 GAN,
1117 CoordinateBased,
1119 Periodic,
1121 Autoencoder,
1123}
1124
1125#[derive(Debug, Clone, Copy, PartialEq, Eq)]
1127pub enum ActivationHint {
1128 ReLU,
1130 Tanh,
1132 Sigmoid,
1134 SELU,
1136 Swish,
1138 GELU,
1140 Sine,
1142 Linear,
1144}
1145
1146pub fn auto_init(
1172 shape: &[usize],
1173 arch: ArchitectureHint,
1174 activation: ActivationHint,
1175 layer_depth: Option<usize>,
1176) -> Result<Tensor> {
1177 match (arch, activation) {
1178 (ArchitectureHint::Periodic, ActivationHint::Sine) | (_, ActivationHint::Sine) => {
1180 let is_first_layer = layer_depth.unwrap_or(0) == 0;
1181 if is_first_layer {
1182 siren_init(shape, 1.0, 30.0)
1183 } else {
1184 siren_init(shape, 6.0, 1.0)
1185 }
1186 }
1187
1188 (ArchitectureHint::CoordinateBased, _) => coordinate_mlp_init(shape, 1.0),
1190
1191 (ArchitectureHint::VeryDeep, ActivationHint::ReLU) => {
1193 if let Some(depth) = layer_depth {
1194 fixup_init(shape, depth, depth / 2, true)
1196 } else {
1197 kaiming_normal_with_nonlinearity(shape, FanMode::FanIn, Nonlinearity::ReLU)
1198 }
1199 }
1200
1201 (ArchitectureHint::Residual, _) => {
1203 if layer_depth.is_some() {
1204 rezero_init(shape)
1206 } else {
1207 kaiming_normal_with_nonlinearity(shape, FanMode::FanIn, Nonlinearity::ReLU)
1208 }
1209 }
1210
1211 (ArchitectureHint::Recurrent, _) => orthogonal_init(shape, 1.0),
1213
1214 (ArchitectureHint::Transformer, _) => xavier_uniform(shape),
1216
1217 (ArchitectureHint::GAN, _) => {
1219 gan_balanced_init(shape, true)
1221 }
1222
1223 (ArchitectureHint::Convolutional, ActivationHint::ReLU) => {
1225 kaiming_normal_with_nonlinearity(shape, FanMode::FanIn, Nonlinearity::ReLU)
1226 }
1227 (ArchitectureHint::Convolutional, ActivationHint::Tanh) => xavier_normal(shape),
1228 (ArchitectureHint::Convolutional, ActivationHint::Sigmoid) => xavier_normal(shape),
1229 (ArchitectureHint::Convolutional, ActivationHint::SELU) => lecun_normal(shape),
1230 (ArchitectureHint::Convolutional, ActivationHint::Swish)
1231 | (ArchitectureHint::Convolutional, ActivationHint::GELU) => {
1232 kaiming_normal_with_nonlinearity(shape, FanMode::FanIn, Nonlinearity::Swish)
1233 }
1234
1235 (ArchitectureHint::Feedforward, ActivationHint::ReLU)
1237 | (ArchitectureHint::Autoencoder, ActivationHint::ReLU) => {
1238 kaiming_uniform_with_nonlinearity(shape, FanMode::FanIn, Nonlinearity::ReLU)
1239 }
1240 (ArchitectureHint::Feedforward, ActivationHint::Tanh)
1241 | (ArchitectureHint::Autoencoder, ActivationHint::Tanh) => xavier_uniform(shape),
1242 (ArchitectureHint::Feedforward, ActivationHint::Sigmoid)
1243 | (ArchitectureHint::Autoencoder, ActivationHint::Sigmoid) => xavier_uniform(shape),
1244 (ArchitectureHint::Feedforward, ActivationHint::SELU)
1245 | (ArchitectureHint::Autoencoder, ActivationHint::SELU) => lecun_uniform(shape),
1246 (ArchitectureHint::Feedforward, ActivationHint::Swish)
1247 | (ArchitectureHint::Feedforward, ActivationHint::GELU)
1248 | (ArchitectureHint::Autoencoder, ActivationHint::Swish)
1249 | (ArchitectureHint::Autoencoder, ActivationHint::GELU) => {
1250 kaiming_uniform_with_nonlinearity(shape, FanMode::FanIn, Nonlinearity::Swish)
1251 }
1252 (ArchitectureHint::Feedforward, ActivationHint::Linear) | (_, ActivationHint::Linear) => {
1253 xavier_uniform(shape)
1254 }
1255
1256 _ => xavier_uniform(shape),
1258 }
1259}
1260
1261pub fn recommend_init_method(
1274 arch: ArchitectureHint,
1275 activation: ActivationHint,
1276 layer_depth: Option<usize>,
1277) -> InitMethod {
1278 match (arch, activation) {
1279 (ArchitectureHint::Periodic, ActivationHint::Sine) | (_, ActivationHint::Sine) => {
1281 let is_first_layer = layer_depth.unwrap_or(0) == 0;
1282 if is_first_layer {
1283 InitMethod::SIREN { c: 1.0, w0: 30.0 }
1284 } else {
1285 InitMethod::SIREN { c: 6.0, w0: 1.0 }
1286 }
1287 }
1288
1289 (ArchitectureHint::VeryDeep, ActivationHint::ReLU)
1291 | (ArchitectureHint::Residual, ActivationHint::ReLU) => InitMethod::KaimingNormal {
1292 mode: FanMode::FanIn,
1293 nonlinearity: Nonlinearity::ReLU,
1294 },
1295
1296 (ArchitectureHint::Recurrent, _) => InitMethod::Orthogonal { gain: 1.0 },
1298
1299 (ArchitectureHint::Transformer, _) => InitMethod::XavierUniform { gain: 1.0 },
1301
1302 (ArchitectureHint::Convolutional, ActivationHint::ReLU) => InitMethod::KaimingNormal {
1304 mode: FanMode::FanIn,
1305 nonlinearity: Nonlinearity::ReLU,
1306 },
1307
1308 (_, ActivationHint::SELU) => InitMethod::LecunNormal,
1310
1311 (_, ActivationHint::Tanh) | (_, ActivationHint::Sigmoid) => {
1313 InitMethod::XavierUniform { gain: 1.0 }
1314 }
1315
1316 _ => InitMethod::XavierUniform { gain: 1.0 },
1318 }
1319}
1320
1321#[cfg(test)]
1322mod tests {
1323 use super::*;
1324
1325 #[test]
1326 fn test_fan_calculation() {
1327 let (fan_in, fan_out) = calculate_fan_in_fan_out(&[64, 32, 3, 3]).unwrap();
1328 assert_eq!(fan_in, 32 * 3 * 3);
1329 assert_eq!(fan_out, 64 * 3 * 3);
1330 }
1331
1332 #[test]
1335 fn test_box_muller_survives_a_zero_uniform() {
1336 assert!(box_muller(0.0, 0.5).is_finite());
1337 assert!(box_muller(0.0, 0.0).is_finite());
1338 assert!(box_muller(0.0, 1.0).is_finite());
1339 let expected = (-2.0f32 * 0.25f32.ln()).sqrt() * (2.0 * std::f32::consts::PI * 0.75).cos();
1341 assert!((box_muller(0.25, 0.75) - expected).abs() < 1e-6);
1342 }
1343
1344 #[test]
1345 fn test_xavier_uniform() {
1346 let tensor = xavier_uniform(&[10, 5]).unwrap();
1347 assert_eq!(tensor.shape().dims(), &[10, 5]);
1348 }
1349
1350 #[test]
1351 fn test_init_method_enum() {
1352 let method = InitMethod::XavierUniform { gain: 1.0 };
1353 let tensor = method.initialize(&[5, 3]).unwrap();
1354 assert_eq!(tensor.shape().dims(), &[5, 3]);
1355 }
1356
1357 #[test]
1358 fn test_nonlinearity_gains() {
1359 assert!((Nonlinearity::ReLU.gain() - (2.0_f32).sqrt()).abs() < 1e-6);
1360 assert!((Nonlinearity::Linear.gain() - 1.0).abs() < 1e-6);
1361 assert!(
1362 (Nonlinearity::LeakyReLU {
1363 negative_slope: 0.01
1364 }
1365 .gain()
1366 - (2.0 / (1.0 + 0.01_f32.powi(2))).sqrt())
1367 .abs()
1368 < 1e-6
1369 );
1370 }
1371
1372 #[test]
1373 fn test_sparse_initialization() {
1374 let tensor = sparse_init(&[10, 10], 0.5, 1.0).unwrap();
1375 assert_eq!(tensor.shape().dims(), &[10, 10]);
1376
1377 assert!(sparse_init(&[10, 10], 1.5, 1.0).is_err());
1379 assert!(sparse_init(&[10, 10], -0.1, 1.0).is_err());
1380 }
1381
1382 #[test]
1383 fn test_variance_scaling() {
1384 let tensor =
1386 variance_scaling(&[10, 5], 2.0, FanMode::FanIn, Distribution::Uniform).unwrap();
1387 assert_eq!(tensor.shape().dims(), &[10, 5]);
1388
1389 let tensor = variance_scaling(&[10, 5], 2.0, FanMode::FanIn, Distribution::Normal).unwrap();
1391 assert_eq!(tensor.shape().dims(), &[10, 5]);
1392
1393 let tensor =
1395 variance_scaling(&[10, 5], 2.0, FanMode::FanIn, Distribution::TruncatedNormal).unwrap();
1396 assert_eq!(tensor.shape().dims(), &[10, 5]);
1397 }
1398
1399 #[test]
1400 fn test_dirac_initialization() {
1401 let tensor = dirac_init(&[16, 16, 3]).unwrap();
1403 assert_eq!(tensor.shape().dims(), &[16, 16, 3]);
1404
1405 assert!(dirac_init(&[10, 10]).is_err());
1407 }
1408
1409 #[test]
1410 fn test_siren_initialization() {
1411 let tensor = siren_init(&[10, 5], 1.0, 30.0).unwrap();
1413 assert_eq!(tensor.shape().dims(), &[10, 5]);
1414
1415 let tensor = siren_init(&[10, 5], 6.0, 1.0).unwrap();
1417 assert_eq!(tensor.shape().dims(), &[10, 5]);
1418
1419 assert!(siren_init(&[10], 6.0, 1.0).is_err());
1421 }
1422
1423 #[test]
1424 fn test_init_method_builders() {
1425 let method = InitMethod::xavier_uniform();
1427 assert_eq!(method.name(), "Xavier Uniform");
1428
1429 let method = InitMethod::kaiming_normal().with_fan_mode(FanMode::FanOut);
1430 assert_eq!(method.name(), "Kaiming Normal");
1431
1432 let method = InitMethod::orthogonal().with_gain(2.0);
1433 assert_eq!(method.name(), "Orthogonal");
1434
1435 let method = InitMethod::siren_first_layer();
1436 assert_eq!(method.name(), "SIREN");
1437
1438 let method = InitMethod::dirac();
1439 assert_eq!(method.name(), "Dirac");
1440 }
1441
1442 #[test]
1443 fn test_init_method_enum_variants() {
1444 let method = InitMethod::VarianceScaling {
1446 scale: 2.0,
1447 mode: FanMode::FanIn,
1448 distribution: Distribution::Normal,
1449 };
1450 let tensor = method.initialize(&[10, 5]).unwrap();
1451 assert_eq!(tensor.shape().dims(), &[10, 5]);
1452
1453 let method = InitMethod::Dirac;
1454 let tensor = method.initialize(&[8, 8, 3]).unwrap();
1455 assert_eq!(tensor.shape().dims(), &[8, 8, 3]);
1456
1457 let method = InitMethod::SIREN { c: 6.0, w0: 1.0 };
1458 let tensor = method.initialize(&[10, 5]).unwrap();
1459 assert_eq!(tensor.shape().dims(), &[10, 5]);
1460 }
1461
1462 #[test]
1463 fn test_fixup_initialization() {
1464 let tensor = fixup_init(&[10, 10], 50, 10, true).unwrap();
1466 assert_eq!(tensor.shape().dims(), &[10, 10]);
1467
1468 let tensor = fixup_init(&[10, 10], 50, 10, false).unwrap();
1470 assert_eq!(tensor.shape().dims(), &[10, 10]);
1471
1472 let tensor = fixup_init(&[5, 5], 2, 1, true).unwrap();
1474 assert_eq!(tensor.shape().dims(), &[5, 5]);
1475 }
1476
1477 #[test]
1478 fn test_rezero_initialization() {
1479 let tensor = rezero_init(&[10, 5]).unwrap();
1481 assert_eq!(tensor.shape().dims(), &[10, 5]);
1482
1483 let alpha = rezero_alpha_init().unwrap();
1485 assert_eq!(alpha.shape().dims(), &[1]);
1486 let alpha_val: Vec<f32> = alpha
1488 .to_vec()
1489 .expect("tensor to vec conversion should succeed");
1490 assert_eq!(alpha_val[0], 0.0);
1491 }
1492
1493 #[test]
1494 fn test_delta_orthogonal_initialization() {
1495 let tensor = delta_orthogonal_init(&[10, 10], 1.0).unwrap();
1497 assert_eq!(tensor.shape().dims(), &[10, 10]);
1498
1499 let tensor = delta_orthogonal_init(&[8, 8], 2.0).unwrap();
1501 assert_eq!(tensor.shape().dims(), &[8, 8]);
1502
1503 assert!(delta_orthogonal_init(&[10], 1.0).is_err());
1505 }
1506
1507 #[test]
1508 fn test_metainit() {
1509 let tensor = metainit(&[10, 10], 0.8, 0.05).unwrap();
1511 assert_eq!(tensor.shape().dims(), &[10, 10]);
1512
1513 let values: Vec<f32> = tensor
1515 .to_vec()
1516 .expect("tensor to vec conversion should succeed");
1517 let zero_count = values.iter().filter(|&&v| v == 0.0).count();
1518 let sparsity_ratio = zero_count as f32 / values.len() as f32;
1519 assert!(sparsity_ratio > 0.6 && sparsity_ratio < 0.95);
1522
1523 assert!(metainit(&[10, 10], 1.5, 0.05).is_err()); assert!(metainit(&[10, 10], -0.1, 0.05).is_err()); assert!(metainit(&[10, 10], 0.8, -0.05).is_err()); }
1528
1529 #[test]
1530 fn test_lsuv_initialization() {
1531 let tensor = lsuv_init(&[10, 10]).unwrap();
1533 assert_eq!(tensor.shape().dims(), &[10, 10]);
1534
1535 let tensor = lsuv_init(&[64, 32]).unwrap();
1537 assert_eq!(tensor.shape().dims(), &[64, 32]);
1538 }
1539
1540 #[test]
1541 fn test_zero_centered_variance_init() {
1542 let tensor = zero_centered_variance_init(&[10, 10], 1.0).unwrap();
1544 assert_eq!(tensor.shape().dims(), &[10, 10]);
1545
1546 let tensor = zero_centered_variance_init(&[20, 20], 0.5).unwrap();
1547 assert_eq!(tensor.shape().dims(), &[20, 20]);
1548
1549 assert!(zero_centered_variance_init(&[10, 10], 0.0).is_err());
1551 assert!(zero_centered_variance_init(&[10, 10], -1.0).is_err());
1552 }
1553
1554 #[test]
1555 fn test_gan_balanced_initialization() {
1556 let gen_tensor = gan_balanced_init(&[10, 10], true).unwrap();
1558 assert_eq!(gen_tensor.shape().dims(), &[10, 10]);
1559
1560 let disc_tensor = gan_balanced_init(&[10, 10], false).unwrap();
1562 assert_eq!(disc_tensor.shape().dims(), &[10, 10]);
1563
1564 }
1567
1568 #[test]
1569 fn test_coordinate_mlp_initialization() {
1570 let tensor = coordinate_mlp_init(&[10, 3], 1.0).unwrap();
1572 assert_eq!(tensor.shape().dims(), &[10, 3]);
1573
1574 let tensor = coordinate_mlp_init(&[64, 32], 30.0).unwrap();
1576 assert_eq!(tensor.shape().dims(), &[64, 32]);
1577
1578 assert!(coordinate_mlp_init(&[10], 1.0).is_err());
1580 }
1581
1582 #[test]
1583 fn test_auto_init() {
1584 let tensor = auto_init(
1586 &[10, 5],
1587 ArchitectureHint::Feedforward,
1588 ActivationHint::ReLU,
1589 None,
1590 )
1591 .unwrap();
1592 assert_eq!(tensor.shape().dims(), &[10, 5]);
1593
1594 let tensor = auto_init(
1596 &[64, 32, 3, 3],
1597 ArchitectureHint::Convolutional,
1598 ActivationHint::ReLU,
1599 None,
1600 )
1601 .unwrap();
1602 assert_eq!(tensor.shape().dims(), &[64, 32, 3, 3]);
1603
1604 let tensor = auto_init(
1606 &[128, 256],
1607 ArchitectureHint::Recurrent,
1608 ActivationHint::Tanh,
1609 None,
1610 )
1611 .unwrap();
1612 assert_eq!(tensor.shape().dims(), &[128, 256]);
1613
1614 let tensor = auto_init(
1616 &[512, 512],
1617 ArchitectureHint::Transformer,
1618 ActivationHint::GELU,
1619 None,
1620 )
1621 .unwrap();
1622 assert_eq!(tensor.shape().dims(), &[512, 512]);
1623
1624 let tensor = auto_init(
1626 &[32, 16],
1627 ArchitectureHint::Periodic,
1628 ActivationHint::Sine,
1629 Some(0),
1630 )
1631 .unwrap();
1632 assert_eq!(tensor.shape().dims(), &[32, 16]);
1633
1634 let tensor = auto_init(
1636 &[256, 256],
1637 ArchitectureHint::VeryDeep,
1638 ActivationHint::ReLU,
1639 Some(100),
1640 )
1641 .unwrap();
1642 assert_eq!(tensor.shape().dims(), &[256, 256]);
1643
1644 let tensor = auto_init(
1646 &[100, 784],
1647 ArchitectureHint::GAN,
1648 ActivationHint::ReLU,
1649 None,
1650 )
1651 .unwrap();
1652 assert_eq!(tensor.shape().dims(), &[100, 784]);
1653
1654 let tensor = auto_init(
1656 &[64, 3],
1657 ArchitectureHint::CoordinateBased,
1658 ActivationHint::ReLU,
1659 None,
1660 )
1661 .unwrap();
1662 assert_eq!(tensor.shape().dims(), &[64, 3]);
1663 }
1664
1665 #[test]
1666 fn test_recommend_init_method() {
1667 let method =
1669 recommend_init_method(ArchitectureHint::Feedforward, ActivationHint::ReLU, None);
1670 matches!(method, InitMethod::KaimingNormal { .. });
1671
1672 let method =
1674 recommend_init_method(ArchitectureHint::Transformer, ActivationHint::GELU, None);
1675 matches!(method, InitMethod::XavierUniform { .. });
1676
1677 let method = recommend_init_method(ArchitectureHint::Recurrent, ActivationHint::Tanh, None);
1679 matches!(method, InitMethod::Orthogonal { .. });
1680
1681 let method =
1683 recommend_init_method(ArchitectureHint::Periodic, ActivationHint::Sine, Some(0));
1684 matches!(method, InitMethod::SIREN { .. });
1685
1686 let method =
1688 recommend_init_method(ArchitectureHint::Feedforward, ActivationHint::SELU, None);
1689 matches!(method, InitMethod::LecunNormal);
1690 }
1691
1692 #[test]
1693 fn test_architecture_hints() {
1694 assert_ne!(
1696 ArchitectureHint::Feedforward,
1697 ArchitectureHint::Convolutional
1698 );
1699 assert_ne!(ArchitectureHint::Recurrent, ArchitectureHint::Transformer);
1700 assert_ne!(ArchitectureHint::Residual, ArchitectureHint::VeryDeep);
1701 assert_ne!(ArchitectureHint::GAN, ArchitectureHint::CoordinateBased);
1702 assert_ne!(ArchitectureHint::Periodic, ArchitectureHint::Autoencoder);
1703 }
1704
1705 #[test]
1706 fn test_activation_hints() {
1707 assert_ne!(ActivationHint::ReLU, ActivationHint::Tanh);
1709 assert_ne!(ActivationHint::Sigmoid, ActivationHint::SELU);
1710 assert_ne!(ActivationHint::Swish, ActivationHint::GELU);
1711 assert_ne!(ActivationHint::Sine, ActivationHint::Linear);
1712 }
1713}