use num_complex::Complex;
use crate::complex::{exp_m1, inv, times_i};
use crate::mie::kernel::{Outer, OuterDerivs, Side, State, int, ratios, real};
use crate::scalar::Real;
#[derive(Clone, Copy, Debug)]
struct Arg<T> {
z: Complex<T>,
inv_z: Complex<T>,
pi: Complex<T>,
w: Complex<T>,
w_n: Complex<T>,
}
fn two_i<T: Real>(z: Complex<T>) -> Complex<T> {
Complex::new(-(z.im * 2.0), z.re * 2.0)
}
impl<T: Real> Arg<T> {
fn new(z: Complex<T>, chi: bool) -> Self {
let inv_z = inv(z);
let (pi, w) = if chi {
let (s, c) = (z.sin(), z.cos());
(s * c, inv_z + s * inv(c))
} else {
(
-exp_m1(two_i(z)).scale(T::from_f64(0.5)),
inv_z - Complex::new(T::zero(), T::one()),
)
};
Self {
z,
inv_z,
pi,
w,
w_n: real(T::zero()),
}
}
fn step(&mut self, n: usize, u_n: Complex<T>) {
self.w_n = self.w;
self.pi = self.pi * u_n * self.w;
self.w = self.inv_z.scale(int(2 * n + 1)) - inv(self.w);
}
fn d1(&self, n: usize, u_next: Complex<T>) -> Complex<T> {
self.inv_z.scale(int(n + 1)) - u_next
}
fn d3(&self, n: usize) -> Complex<T> {
self.inv_z.scale(int(n + 1)) - self.w
}
fn d1_prime(&self, n: usize, u: Complex<T>) -> Complex<T> {
let iz = self.inv_z;
(u.scale(T::from_f64(2.0)) * iz - iz * iz).scale(int(n + 1)) - (real(T::one()) + u * u)
}
fn d1_plus(&self, n: usize, u: Complex<T>) -> Complex<T> {
u.scale(int(2 * n + 1)) - self.z * (real(T::one()) + u * u)
}
}
fn norm1<T: Real>(z: Complex<T>) -> T {
z.re.abs() + z.im.abs()
}
struct Step<T> {
u_prev: Complex<T>,
u1: Complex<T>,
d1i: Complex<T>,
d3i: Complex<T>,
d1o: Complex<T>,
d3o: Complex<T>,
pio: Complex<T>,
p: Complex<T>,
w: Complex<T>,
derivatives: bool,
n: usize,
u2: Complex<T>,
s: Complex<T>,
a1: Arg<T>,
a2: Arg<T>,
}
#[derive(Clone, Copy, Debug)]
pub(super) struct Sens<T> {
hv: Complex<T>,
r2: Complex<T>,
em: Complex<T>,
hp: Complex<T>,
hplus: Complex<T>,
}
fn side<T: Real>(
mi: Complex<T>,
mo: Complex<T>,
prev: Side<T>,
h: Complex<T>,
lead: Complex<T>,
s: &Step<T>,
lead_is_a: bool,
) -> (Side<T>, Complex<T>, Option<Sens<T>>) {
let mh = mi * h;
let g1 = match prev {
Side::Delta(delta) => lead - mi * s.u_prev + mo * s.u1 + mi * delta,
Side::Direct(_) => mh - mo * s.d1i,
};
let g2 = mh - mo * s.d3i;
let inv_den = inv(s.pio * g2 - s.p * g1);
let delta = -(s.w * s.p * g1 * inv_den * inv(s.pio));
let hd = s.d1o + delta;
let (out, h_out) = if norm1(hd) * 4.0 >= norm1(s.d1o).max(norm1(delta)) {
(Side::Delta(delta), hd)
} else {
let hp = s.d3o - s.w * g2 * inv_den;
(Side::Direct(hp), hp)
};
if !s.derivatives {
return (out, h_out, None);
}
let two = T::from_f64(2.0);
let inv_mo = inv(mo);
let t = mh * inv_mo;
let r = -(mo * s.w * s.s * inv_den);
let e = g1 * inv_mo;
let sigma = if lead_is_a { T::one() } else { -T::one() };
let d1 = s.d1i;
let z1 = s.a1.z;
let own = if lead_is_a {
d1.scale(two) - s.a1.d1_plus(s.n, s.u1) } else {
-s.a1.d1_plus(s.n, s.u1) };
let em = if norm1(t) * 4.0 >= norm1(d1).max(norm1(e)) {
own + e * (real(sigma) + z1 * (d1.scale(two) + e))
} else {
let iz = s.a1.inv_z;
let v = real(int::<T>(s.n) * int::<T>(s.n + 1)) * iz * iz - real(T::one());
t.scale(sigma) - z1 * (v - t * t)
};
let (hp, hplus) = match out {
Side::Delta(delta) => {
let d2 = s.d1o.scale(two) + delta;
(
s.a2.d1_prime(s.n, s.u2) - delta * d2,
s.a2.d1_plus(s.n, s.u2) + delta * (real(T::one()) - s.a2.z * d2),
)
}
Side::Direct(hv) => {
let iz = s.a2.inv_z;
let v = real(int::<T>(s.n) * int::<T>(s.n + 1)) * iz * iz - real(T::one());
let hp = v - hv * hv;
(hp, hv + s.a2.z * hp)
}
};
let sens = Sens {
hv: h_out,
r2: r * r,
em,
hp,
hplus,
};
(out, h_out, Some(sens))
}
#[derive(Clone, Debug)]
pub(super) struct LayerGrad<T> {
pub(super) da_dx: Vec<Complex<T>>,
pub(super) da_dm: Vec<Complex<T>>,
pub(super) db_dx: Vec<Complex<T>>,
pub(super) db_dm: Vec<Complex<T>>,
pub(super) abs_sum: [T; 3],
}
impl<T: Real> LayerGrad<T> {
fn new() -> Self {
Self {
da_dx: Vec::new(),
da_dm: Vec::new(),
db_dx: Vec::new(),
db_dm: Vec::new(),
abs_sum: [T::zero(); 3],
}
}
}
#[derive(Clone, Debug)]
pub(super) struct Kernel<T: Real> {
pub(super) mie: State<T>,
u: Vec<Vec<Complex<T>>>,
scratch: Vec<T>,
inner: Vec<Arg<T>>,
outer_args: Vec<Arg<T>>,
p: Vec<Complex<T>>,
lead: Vec<Complex<T>>,
chi: Vec<bool>,
outer: Vec<Outer<T>>,
s: Vec<Complex<T>>,
sens: Vec<(Sens<T>, Sens<T>)>,
outer_derivs: Vec<OuterDerivs<T>>,
dh: Vec<[Vec<Complex<T>>; 4]>,
pub(super) grads: Vec<LayerGrad<T>>,
}
impl<T: Real> Kernel<T> {
pub(super) fn new() -> Self {
Self {
mie: State::new(),
u: Vec::new(),
scratch: Vec::new(),
inner: Vec::new(),
outer_args: Vec::new(),
p: Vec::new(),
lead: Vec::new(),
chi: Vec::new(),
outer: Vec::new(),
s: Vec::new(),
sens: Vec::new(),
outer_derivs: Vec::new(),
dh: Vec::new(),
grads: Vec::new(),
}
}
pub(super) fn solve(&mut self, x: &[T], m: &[Complex<T>], n: usize, derivatives: bool) {
let layers = x.len();
debug_assert!(layers >= 2 && m.len() == layers);
let last = layers - 1;
self.mie.prepare(x[last], m[last], n);
self.u.resize_with(2 * last, Vec::new);
ratios(&mut self.u[0], &mut self.scratch, m[0], x[0], n);
for l in 1..layers {
ratios(&mut self.u[2 * l - 1], &mut self.scratch, m[l], x[l - 1], n);
if l < last {
ratios(&mut self.u[2 * l], &mut self.scratch, m[l], x[l], n);
}
}
self.inner.clear();
self.outer_args.clear();
self.p.clear();
self.lead.clear();
self.chi.clear();
for l in 1..layers {
let chi = m[l].im.approx_f64() * x[l].approx_f64() < 0.5;
self.chi.push(chi);
let (a, b) = (m[l], m[l - 1]);
self.lead.push((a - b) * (a + b) * inv(a * b));
let (z1, z2) = (m[l].scale(x[l - 1]), m[l].scale(x[l]));
self.inner.push(Arg::new(z1, chi));
self.outer_args.push(Arg::new(z2, chi));
let p0 = if chi {
let c2 = z2.cos();
z1.sin() * inv(z1.cos()) * c2 * c2
} else {
let dx = x[l] - x[l - 1];
let (a, b) = (-(m[l].im * dx * 2.0), m[l].re * dx * 2.0);
let (sin_b, cos_b) = b.sin_cos();
let e = Complex::new(cos_b, sin_b).scale(a.exp());
-(e * exp_m1(two_i(z1))).scale(T::from_f64(0.5))
};
self.p.push(p0);
}
self.s.clear();
if derivatives {
for l in 1..layers {
let (z1, z2) = (m[l].scale(x[l - 1]), m[l].scale(x[l]));
let s0 = if self.chi[l - 1] {
z2.cos() * inv(z1.cos())
} else {
let dx = x[l] - x[l - 1];
let (sin_b, cos_b) = (m[l].re * dx).sin_cos();
Complex::new(cos_b, sin_b).scale((-(m[l].im * dx)).exp())
};
self.s.push(s0);
}
self.dh
.resize_with(layers, || core::array::from_fn(|_| Vec::new()));
self.dh.truncate(layers);
for d in &mut self.dh {
for v in d.iter_mut() {
v.clear();
}
}
self.outer_derivs.clear();
}
let core = Arg::new(m[0].scale(x[0]), false);
self.outer.clear();
let u_z = self.mie.u_z();
for k in 1..=n {
let u_of = |l: usize| -> &[Complex<T>] { if l == last { u_z } else { &self.u[2 * l] } };
for l in 1..layers {
let (u1, u2) = (&self.u[2 * l - 1], u_of(l));
let (ai, ao) = (&mut self.inner[l - 1], &mut self.outer_args[l - 1]);
ai.step(k, u1[k]);
ao.step(k, u2[k]);
self.p[l - 1] = self.p[l - 1] * u1[k] * inv(ai.w_n) * ao.w_n * ao.w_n;
if derivatives {
self.s[l - 1] = self.s[l - 1] * ao.w_n * inv(ai.w_n);
}
}
self.sens.clear();
let mut ha = core.d1(k, self.u[0][k + 1]);
let mut hb = ha;
let zero = Side::Delta(real(T::zero()));
let mut sides = (zero, zero);
for l in 1..layers {
let (ai, ao) = (&self.inner[l - 1], &self.outer_args[l - 1]);
let u_prev = if l == 1 {
&self.u[0]
} else {
&self.u[2 * (l - 1)]
};
let s = Step {
u_prev: u_prev[k + 1],
u1: self.u[2 * l - 1][k + 1],
d1i: ai.d1(k, self.u[2 * l - 1][k + 1]),
d3i: ai.d3(k),
d1o: ao.d1(k, u_of(l)[k + 1]),
d3o: ao.d3(k),
pio: ao.pi,
p: self.p[l - 1],
w: if self.chi[l - 1] {
real(-T::one())
} else {
Complex::new(T::zero(), T::one())
},
derivatives,
n: k,
u2: u_of(l)[k + 1],
s: if derivatives {
self.s[l - 1]
} else {
real(T::zero())
},
a1: *ai,
a2: *ao,
};
let lead = self.lead[l - 1].scale(int::<T>(k + 1) / x[l - 1]);
let (sa, a, ea) = side(m[l], m[l - 1], sides.0, ha, lead, &s, true);
let (sb, b, eb) = side(m[l - 1], m[l], sides.1, hb, real(T::zero()), &s, false);
(ha, hb, sides) = (a, b, (sa, sb));
if let (Some(ea), Some(eb)) = (ea, eb) {
self.sens.push((ea, eb));
}
}
self.outer.push(sides);
if derivatives {
let u0 = self.u[0][k + 1];
let core_h = (core.d1(k, u0), core.d1_plus(k, u0));
let extra = Self::adjoint(&self.sens, &mut self.dh, x, m, k, core_h);
let (la, lb) = self.sens[last - 1];
self.outer_derivs.push(OuterDerivs {
hp: [la.hp, lb.hp],
hplus: [la.hplus, lb.hplus],
extra_m: extra,
});
}
}
let od = derivatives.then_some(&self.outer_derivs[..]);
self.mie
.finish(x[last], m[last], n, derivatives, Some(&self.outer), od);
if derivatives {
self.inner_grads(m[last], n);
}
if m.iter().all(|m| m.im.is_zero()) {
self.mie.abs_sum = T::zero();
self.mie.abs_sum_jacobian[0] = T::zero();
self.mie.abs_sum_jacobian[1] = T::zero();
for g in &mut self.grads {
g.abs_sum[0] = T::zero();
g.abs_sum[1] = T::zero();
}
}
}
fn adjoint(
sens: &[(Sens<T>, Sens<T>)],
dh: &mut [[Vec<Complex<T>>; 4]],
x: &[T],
m: &[Complex<T>],
n: usize,
core: (Complex<T>, Complex<T>),
) -> [Complex<T>; 2] {
let layers = x.len();
let last = layers - 1;
let two = T::from_f64(2.0);
let nn1 = int::<T>(n) * int::<T>(n + 1);
let mut extra = [real(T::zero()); 2];
for side in 0..2 {
let mut dx = vec![real(T::zero()); layers];
let mut dm = vec![real(T::zero()); layers];
let mut lambda = real(T::one());
for l in (1..layers).rev() {
let e = if side == 0 {
sens[l - 1].0
} else {
sens[l - 1].1
};
let (ml, mp) = (m[l], m[l - 1]);
dm[l] = dm[l] + lambda * e.r2 * e.em * inv(ml);
let (h, hplus) = if l == 1 {
core
} else {
let p = if side == 0 {
sens[l - 2].0
} else {
sens[l - 2].1
};
(p.hv, p.hplus)
};
let c = (ml - mp) * (ml + mp);
let w = lambda * e.r2;
let inv_mp = inv(mp);
let (jump, k) = if side == 0 {
let inv_x = x[l - 1].recip();
let j = ml
* c
* (real(nn1 * inv_x * inv_x) * inv(mp * mp * ml * ml)
+ h * h * inv_mp * inv_mp);
(j, -(ml * inv_mp * inv_mp * (h.scale(two) - hplus)))
} else {
let inv_ml = inv(ml);
(c * inv_ml, hplus * inv_ml)
};
dx[l - 1] = dx[l - 1] + w * jump;
dm[l - 1] = dm[l - 1] + w * k;
let (mi, mo) = if side == 0 { (ml, mp) } else { (mp, ml) };
lambda = w * mi * inv(mo);
}
extra[side] = dm[last];
for l in 0..last {
dh[l][2 * side].push(dx[l]);
dh[l][2 * side + 1].push(dm[l]);
}
}
extra
}
fn inner_grads(&mut self, m: Complex<T>, n: usize) {
let last = self.dh.len() - 1;
let two = T::from_f64(2.0);
let inv_m = inv(m);
self.grads.resize_with(last, LayerGrad::new);
self.grads.truncate(last);
let term = |f: Complex<T>, df: Complex<T>, l: Complex<T>, n2: T| {
-((df.im - two * f.im * l.re) * n2)
};
for (l, g) in self.grads.iter_mut().enumerate() {
for v in [&mut g.da_dx, &mut g.da_dm, &mut g.db_dx, &mut g.db_dm] {
v.clear();
}
let mut jac = [T::zero(); 3];
let dh = &self.dh[l];
#[allow(clippy::needless_range_loop)] for k in 0..n {
let [inv_da, inv_db, g_a, g_b, b_a, b_b] = self.mie.stored[k];
let (ida2, idb2) = (inv_da * inv_da, inv_db * inv_db);
let (na, nb) = (inv_da.norm_sqr(), inv_db.norm_sqr());
let ba_x = dh[0][k] * inv_m;
let ba_m = dh[1][k] * inv_m;
let bb_x = m * dh[2][k];
let bb_m = m * dh[3][k];
g.da_dx.push(-(times_i(ba_x) * ida2));
g.da_dm.push(-(times_i(ba_m) * ida2));
g.db_dx.push(-(times_i(bb_x) * idb2));
g.db_dm.push(-(times_i(bb_m) * idb2));
let weight: T = int(2 * k + 3);
let pair = |da: Complex<T>, db: Complex<T>| {
term(b_a, da, da * g_a, na) + term(b_b, db, db * g_b, nb)
};
jac[0] += weight * pair(ba_x, bb_x);
jac[1] += weight * pair(ba_m, bb_m);
jac[2] += weight * pair(times_i(ba_m), times_i(bb_m));
}
g.abs_sum = jac;
}
}
}