#[allow(unused_imports)]
use arael::model::{Param, SelfBlock, CrossBlock, Model};
use arael::simple_lm::LmProblem;
#[arael::model]
struct Pose {
pos: Param<f64>, info_tag: f64, hb_pose: SelfBlock<Pose>,
}
#[arael::model]
#[arael(constraint([pose.hb_pose, hb_root], parent=lm, {
[(pose.pos - testmodel.offset - lm.offset_tag) * testmodel.isigma]
}))]
struct Frine {
#[arael(ref = root.poses)]
pose: arael::refs::Ref<Pose>,
hb_root: CrossBlock<Pose, TestModel>,
}
#[arael::model]
struct Landmark {
offset_tag: f64,
frines: std::vec::Vec<Frine>,
}
#[arael::model]
#[arael(root)]
struct TestModel {
poses: arael::refs::Vec<Pose>,
landmarks: arael::refs::Vec<Landmark>,
offset: Param<f64>,
isigma: f64,
hb: SelfBlock<TestModel>,
}
fn build_model() -> (TestModel, Vec<f64>) {
let mut poses = arael::refs::Vec::new();
poses.push(Pose { pos: Param::new(1.4), info_tag: 0.0, hb_pose: SelfBlock::new() });
poses.push(Pose { pos: Param::new(-0.7), info_tag: 0.0, hb_pose: SelfBlock::new() });
poses.push(Pose { pos: Param::new(2.3), info_tag: 0.0, hb_pose: SelfBlock::new() });
let mut landmarks = arael::refs::Vec::new();
landmarks.push(Landmark {
offset_tag: 0.1,
frines: vec![
Frine { pose: arael::refs::Ref::new(0), hb_root: CrossBlock::new() },
Frine { pose: arael::refs::Ref::new(1), hb_root: CrossBlock::new() },
],
});
landmarks.push(Landmark {
offset_tag: -0.2,
frines: vec![
Frine { pose: arael::refs::Ref::new(1), hb_root: CrossBlock::new() },
Frine { pose: arael::refs::Ref::new(2), hb_root: CrossBlock::new() },
],
});
let mut model = TestModel {
poses, landmarks,
offset: Param::new(0.3),
isigma: 2.0,
hb: SelfBlock::new(),
};
let mut params = Vec::new();
model.serialize64(&mut params);
(model, params)
}
fn numerical_grad_hessian(model: &mut TestModel, params: &[f64]) -> (Vec<f64>, Vec<f64>) {
let n = params.len();
let eps_g = 1e-6;
let eps_h = 1e-4;
let mut grad = vec![0.0; n];
for i in 0..n {
let mut pp = params.to_vec();
pp[i] += eps_g;
let cp = model.calc_cost(&pp);
pp[i] -= 2.0 * eps_g;
let cm = model.calc_cost(&pp);
grad[i] = (cp - cm) / (2.0 * eps_g);
}
let mut hess = vec![0.0; n * n];
for i in 0..n {
for j in i..n {
let h: f64 = if i == j {
let c0 = model.calc_cost(params);
let mut pp = params.to_vec();
pp[i] += eps_h;
let cp = model.calc_cost(&pp);
pp[i] -= 2.0 * eps_h;
let cm = model.calc_cost(&pp);
(cp - 2.0 * c0 + cm) / (eps_h * eps_h)
} else {
let mut pp = params.to_vec();
pp[i] += eps_h; pp[j] += eps_h;
let cpp = model.calc_cost(&pp);
pp[j] -= 2.0 * eps_h;
let cpm = model.calc_cost(&pp);
pp[i] -= 2.0 * eps_h;
let cmm = model.calc_cost(&pp);
pp[j] += 2.0 * eps_h;
let cmp = model.calc_cost(&pp);
(cpp - cpm - cmp + cmm) / (4.0 * eps_h * eps_h)
};
hess[i * n + j] = h;
hess[j * n + i] = h;
}
}
(grad, hess)
}
#[test]
fn test_nested_remote_plus_root_cross_grad_hessian() {
let (mut model, params) = build_model();
let n = params.len();
assert!(n > 0);
let mut ag = vec![0.0; n];
let mut ah = vec![0.0; n * n];
model.calc_grad_hessian_dense(¶ms, &mut ag, &mut ah);
let (ng, nh) = numerical_grad_hessian(&mut model, ¶ms);
let failed = (0..n).any(|i| (ag[i] - ng[i]).abs() >= 5e-4)
|| (0..n*n).any(|idx| (ah[idx] - nh[idx]).abs() >= 5e-3);
if failed {
eprintln!("params = {:?}", params);
eprintln!("analytic grad = {:?}", ag);
eprintln!("numerical grad = {:?}", ng);
for i in 0..n {
for j in 0..n {
let idx = i * n + j;
if (ah[idx] - nh[idx]).abs() >= 5e-3 {
eprintln!("hess[{},{}] analytic={:+.6} numerical={:+.6} diff={:+.6}",
i, j, ah[idx], nh[idx], ah[idx] - nh[idx]);
}
}
}
}
for i in 0..n {
assert!((ag[i] - ng[i]).abs() < 5e-4,
"grad[{}] analytic={} numerical={}", i, ag[i], ng[i]);
}
for i in 0..n {
for j in 0..n {
let idx = i * n + j;
let d = (ah[idx] - nh[idx]).abs();
assert!(d < 5e-3,
"hess[{},{}] analytic={} numerical={} diff={}", i, j, ah[idx], nh[idx], d);
}
}
}