#![allow(
clippy::default_numeric_fallback,
clippy::expect_used,
clippy::float_cmp,
clippy::indexing_slicing
)]
mod ideal_gas {
use autodj::fluid::Dual;
fn calc_gas_state<D: Dual<Value = f64>>([pressure, volume, temperature, moles]: [D; 4]) -> D {
const UGC: f64 = 8.314;
pressure * volume - temperature * moles * D::parameter(UGC)
}
const ATM: f64 = 101_325.;
const GOLDEN: f64 = 1.618_033_988_75;
const KELVIN: f64 = 273.15;
const BODY: f64 = KELVIN + 36.6;
#[test]
fn moles() {
use autodj::prelude::single::*;
let pressure = ATM.into();
let volume = GOLDEN.into();
let temperature = BODY.into();
let moles_initial = 1.0;
let scalar_func = |m| calc_gas_state([pressure, volume, temperature, m]);
let initial = moles_initial.into_variable().map(scalar_func);
let (f, df) = initial.decompose();
let moles = moles_initial - f / df;
let state = moles.into_variable().map(scalar_func);
println!(
r#"
Initial guess: r({moles_initial}) = {initial}
Update-------: r({moles}) = {:e}"#,
state.value()
);
}
#[test]
fn moles_volume() {
use autodj::prelude::array::*;
const W: f64 = 1.5;
const WEIGHTS: [f64; 2] = [W, 1. - W];
let pressure = ATM.into();
let temperature = BODY.into();
let moles_initial = GOLDEN;
let volume_initial = 1.0;
let vector_func = |&[moles, volume]: &[DualNumber<f64, 2>; 2]| {
calc_gas_state([pressure, volume, temperature, moles])
};
let initial = vector_func(&[moles_initial, volume_initial].into_variables());
let moles = moles_initial - WEIGHTS[0] * initial.value() / initial.dual().as_ref()[0];
let volume = volume_initial - WEIGHTS[1] * initial.value() / initial.dual().as_ref()[1];
let update = vector_func(&[moles, volume].into_variables());
println!(
r#"
Initial guess: r({moles_initial}, {volume_initial}) = {initial:e}
Update-------: r({moles}, {volume}) = {:e}"#,
update.value()
);
}
}
mod vector {
use autodj::prelude::vector::*;
use std::ops::{Add, Mul};
#[test]
fn vector_multiple() {
fn sqps(x: &[DualF64]) -> DualF64 {
let add: DualF64 = 1.0.into();
x.iter()
.map(|x| x.powf(2.0).add_impl(&add))
.reduce(Add::add)
.expect("nonzero slice length")
}
let x = vec![1., 2., 3.];
let result = sqps(x.clone().into_variables().as_slice());
println!("f{x:?} ≈ {result:?}");
assert_eq!(result.value(), &17.0);
assert_eq!(result.dual().as_ref(), &[2., 4., 6.]);
}
#[test]
fn sum() {
let x = vec![1., 2., 3., 4., 5.];
let reference: f64 = x.iter().sum();
println!("f({x:?}) = ∑ x_i = {reference}");
let result: DualF64 = x
.clone()
.into_variables()
.into_iter()
.reduce(Add::add)
.expect("nonzero slice length");
println!("f({x:?}) = {result:?}",);
assert_eq!(result.value(), &reference);
}
#[test]
fn product() {
let x = vec![1., 2., 3., 4., 5.];
let reference: f64 = x.iter().product();
println!("f({x:?}) = ∏ x_i = {reference}");
let result = x.clone().into_variables().into_iter().reduce(Mul::mul);
println!("f({x:?}) = {result:?}",);
assert_eq!(
result.map(|result| result.value().to_owned()),
Some(reference)
);
}
#[test]
fn product_owned() {
let zero = [1.0, 2.0, 3.0]
.into_variables()
.iter()
.map(|x| x.sub_impl(&1.0.into()))
.reduce(Mul::mul);
assert_eq!(zero.map(|result| result.value().to_owned()), Some(0.0));
}
#[test]
fn shifted_partial() {
fn shifted_product(x: &[DualF64], threshold: f64) -> Option<DualF64> {
x.iter()
.filter(|x| x.value() < &threshold)
.map(|x| x.sub_impl(&1.0.into()))
.reduce(Mul::mul)
}
let values: Vec<f64> = vec![2., 3., 5., 8.];
let variables = values.clone().into_variables();
let f = shifted_product(variables.as_slice(), 6.).expect("At least two variables");
println!("f({values:?}) = {f:?}");
assert_eq!(f.value(), &8.);
assert_eq!(f.dual().as_ref().len(), variables.len());
assert_eq!(f.dual().as_ref().last(), Some(&0.));
}
#[test]
fn div() {
let variables = [1., 2.].into_variables();
let x = &variables[0];
let y = &variables[1];
let (f, df) = (x.div_impl(y)).decompose();
assert_eq!(df.as_ref(), &[0.5, -0.25]);
assert_eq!(f, 0.5);
}
}