use tch::Tensor;
pub fn low_pass_filter_gradients(
named_parameters: &[(String, Tensor)],
tau_ratio: f64,
skip_bias: bool,
in_place: bool,
) {
for (_, param) in named_parameters.iter() {
if !param.requires_grad() || !param.grad().defined() {
continue;
}
let grad = param.grad();
if skip_bias && grad.dim() < 2 {
continue;
}
let shape = grad.size();
let device = grad.device();
let grad_complex = grad.to_kind(tch::Kind::ComplexFloat);
let grad_fft = grad_complex.fft_fftn(
None::<&[i64]>,
None::<&[i64]>,
"backward"
);
let mut grad_fft_shifted = grad_fft;
for (i, &dim) in shape.iter().enumerate() {
let shift = dim / 2;
grad_fft_shifted = grad_fft_shifted.roll(&[shift as i64], &[i as i64]);
}
let mut mask_low = Tensor::ones(shape.as_slice(), (tch::Kind::Bool, device));
for (axis, &dim) in shape.iter().enumerate() {
let c = std::cmp::max(1, (dim as f64 * tau_ratio) as i64);
let start = std::cmp::max(0, dim / 2 - c);
let end = std::cmp::min(dim, dim / 2 + c);
let mut idx_mask = vec![false; dim as usize];
for i in start..end {
idx_mask[i as usize] = true;
}
let axis_low = Tensor::from_slice(&idx_mask).to_device(device);
let mut view_shape = vec![1; shape.len()];
view_shape[axis] = dim;
let axis_low = axis_low.view(view_shape.as_slice());
mask_low = mask_low.logical_and(&axis_low);
}
let filtered_fft = grad_fft_shifted.where_self(&mask_low, &Tensor::zeros_like(&grad_fft_shifted));
let mut unshifted = filtered_fft;
for (i, &dim) in shape.iter().enumerate() {
let shift = (dim + 1) / 2;
unshifted = unshifted.roll(&[shift as i64], &[i as i64]);
}
let grad_filtered = unshifted.fft_ifftn(
None::<&[i64]>,
None::<&[i64]>,
"backward"
).real();
if in_place {
let _ = param.grad().copy_(&grad_filtered);
} else {
let _ = param.grad().copy_(&grad_filtered);
}
}
}