use crate::{Shape, Tape, Tensor};
use crate::Module;
use super::LayerNorm;
#[test]
fn new_allocates_scale_shift_and_epsilon() {
let tape = Tape::new();
let norm = LayerNorm::new(
&tape,
Tensor::filled([2], 1.0_f64),
Tensor::filled([2], 0.0),
Tensor::filled([], 1e-5),
);
assert_eq!(tape.len(), 3);
assert_eq!(norm.parameters().count(), 2);
}
#[test]
#[should_panic(expected = "must be rank 1")]
fn new_rejects_non_vector_scale() {
let tape = Tape::new();
LayerNorm::new(
&tape,
Tensor::filled([2, 2], 1.0_f64),
Tensor::filled([2], 0.0),
Tensor::filled([], 1e-5),
);
}
#[test]
#[should_panic(expected = "single value")]
fn new_rejects_multi_value_epsilon() {
let tape = Tape::new();
LayerNorm::new(
&tape,
Tensor::filled([2], 1.0_f64),
Tensor::filled([2], 0.0),
Tensor::filled([2], 1e-5),
);
}
#[test]
fn express_standardizes_every_sample() {
let tape = Tape::new();
let norm = LayerNorm::new(
&tape,
Tensor::filled([2], 1.0_f64),
Tensor::filled([2], 0.0),
Tensor::filled([], 0.0),
);
let input = tape.leaf(Tensor::new([2, 2], [1.0, 3.0, 2.0, 6.0]));
let output = norm.express(input);
assert_eq!(output.shape(), Shape::new([2, 2]));
let output = output.symbol();
let network = tape.into_network();
let run = network.forward(&network.parameters(), []);
assert_eq!(run.of(output).to_vec(), &[-1.0, 1.0, -1.0, 1.0]);
}
#[test]
fn express_applies_the_learned_affine() {
let tape = Tape::new();
let norm = LayerNorm::new(
&tape,
Tensor::new([2], [2.0_f64, 3.0]),
Tensor::new([2], [10.0, 20.0]),
Tensor::filled([], 0.0),
);
let input = tape.leaf(Tensor::new([2, 2], [1.0, 3.0, 2.0, 6.0]));
let output = norm.express(input).symbol();
let network = tape.into_network();
let run = network.forward(&network.parameters(), []);
assert_eq!(run.of(output).to_vec(), &[8.0, 23.0, 8.0, 23.0]);
}
#[test]
fn samples_normalize_independently() {
let tape = Tape::new();
let norm = LayerNorm::new(
&tape,
Tensor::filled([2], 1.0_f64),
Tensor::filled([2], 0.0),
Tensor::filled([], 0.0),
);
let lone = tape.leaf(Tensor::new([1, 2], [1.0, 3.0]));
let paired = tape.leaf(Tensor::new([2, 2], [1.0, 3.0, -100.0, 900.0]));
let lone_output = norm.express(lone).symbol();
let paired_output = norm.express(paired).symbol();
let network = tape.into_network();
let run = network.forward(&network.parameters(), []);
assert_eq!(
run.of(lone_output).to_vec(),
run.of(paired_output).to_vec()[..2]
);
}
#[test]
fn express_records_tensor_granularity() {
let tape = Tape::new();
let norm = LayerNorm::new(
&tape,
Tensor::filled([2], 1.0_f64),
Tensor::filled([2], 0.0),
Tensor::filled([], 0.0),
);
let input = tape.leaf(Tensor::new([3, 2], vec![1.0; 6]));
let nodes_before = tape.len();
norm.express(input);
assert_eq!(tape.len(), nodes_before + 18);
}
#[test]
#[should_panic(expected = "disagree on features")]
fn express_rejects_mismatched_features() {
let tape = Tape::new();
let norm = LayerNorm::new(
&tape,
Tensor::filled([2], 1.0_f64),
Tensor::filled([2], 0.0),
Tensor::filled([], 0.0),
);
let input = tape.leaf(Tensor::new([2, 3], vec![1.0; 6]));
norm.express(input);
}
#[test]
fn gradients_flow_through_the_sample_statistics() {
let tape = Tape::new();
let norm = LayerNorm::new(
&tape,
Tensor::filled([2], 1.0_f64),
Tensor::filled([2], 0.0),
Tensor::filled([], 3.0),
);
let input = tape.leaf(Tensor::new([1, 2], [3.0, 1.0]));
let output = norm.express(input);
let target = output.narrow(1, 0, 1).sum();
let (target, input) = (target.symbol(), input.symbol());
let network = tape.into_network();
let run = network.forward(&network.parameters(), []);
let gradients = run.backward(target);
let computed = gradients.of(input).to_vec();
assert!((computed[0] - 0.1875).abs() < 1e-12);
assert!((computed[1] + 0.1875).abs() < 1e-12);
let parameters: Vec<_> = norm.parameters().collect();
assert_eq!(gradients.of(parameters[0]).to_vec(), &[0.5, 0.0]);
assert_eq!(gradients.of(parameters[1]).to_vec(), &[1.0, 0.0]);
}