use arael::info;
use arael::model::{Model, Param, SelfBlock};
use arael::simple_lm::{self, LmConfig, LmProblem};
use arael_sym::E;
#[arael::function]
fn sigmoid(x: E) -> E {
1.0 / (1.0 + exp(-x))
}
#[arael::function(my_safe_asin,
derivs = [1.0 / sqrt(identity(1.0 - x * x) + 1e-12)])]
fn my_safe_asin_eval(x: f64) -> f64 {
x.clamp(-1.0, 1.0).asin()
}
#[arael::model]
#[arael(root, jacobian)]
#[arael(constraint(hb, name = "sigmoid_eq_0p8", {
// sigmoid(x) - 0.8 = 0 => x = logit(0.8) ~= 1.3863
[(sigmoid(m.x) - 0.8) * m.isigma]
}))]
#[arael(constraint(hb, name = "asin_eq_0p5", {
// my_safe_asin(x) - 0.5 = 0 => x = sin(0.5) ~= 0.4794
[(my_safe_asin(m.x) - 0.5) * m.isigma]
}))]
struct M {
x: Param<f64>,
isigma: f64,
hb: SelfBlock<M>,
}
fn main() {
let x_sym = arael_sym::symbol("x");
info!("sigmoid(x) symbolic form: {}", sigmoid(x_sym.clone()));
info!("my_safe_asin(x) symbolic form: {}", my_safe_asin(x_sym));
info!("");
let mut m = M {
x: Param::new(0.0),
isigma: 1.0,
hb: SelfBlock::new(),
};
let mut params = Vec::new();
m.serialize64(&mut params);
info!("Start x = {:.6}, cost = {:.6}", params[0], m.calc_cost(¶ms));
let config = LmConfig::<f64> {
verbose: true,
max_iters: 50,
..Default::default()
};
let result = simple_lm::solve(¶ms, &mut m, &config);
m.deserialize64(&result.x);
let r1 = sigmoid_f64(m.x.value) - 0.8;
let r2 = my_safe_asin_eval(m.x.value) - 0.5;
info!("");
info!("LM: {} iterations, cost {:.6} -> {:.6}",
result.iterations, result.start_cost, result.end_cost);
info!("x = {:.6}", m.x.value);
info!("sigmoid(x) - 0.8 = {:+.6}", r1);
info!("my_safe_asin(x) - 0.5 = {:+.6}", r2);
info!("(balanced fit: neither residual goes to zero alone)");
}
fn sigmoid_f64(x: f64) -> f64 {
1.0 / (1.0 + (-x).exp())
}