pub trait JetField: Clone {
fn value(&self) -> f64;
fn add(&self, o: &Self) -> Self;
fn sub(&self, o: &Self) -> Self;
fn mul(&self, o: &Self) -> Self;
fn neg(&self) -> Self;
fn scale(&self, s: f64) -> Self;
fn compose_unary(&self, d: [f64; 5]) -> Self;
fn constant_like(&self, v: f64) -> Self {
self.compose_unary([v, 0.0, 0.0, 0.0, 0.0])
}
fn with_value(&self, v: f64) -> Self {
self.sub(&self.constant_like(self.value()))
.add(&self.constant_like(v))
}
}
pub trait JetFieldConst: JetField + Copy {
fn from_f64(x: f64) -> Self;
}
impl JetField for f64 {
#[inline]
fn value(&self) -> f64 {
*self
}
#[inline]
fn add(&self, o: &Self) -> Self {
*self + *o
}
#[inline]
fn sub(&self, o: &Self) -> Self {
*self - *o
}
#[inline]
fn mul(&self, o: &Self) -> Self {
*self * *o
}
#[inline]
fn neg(&self) -> Self {
-*self
}
#[inline]
fn scale(&self, s: f64) -> Self {
*self * s
}
#[inline]
fn compose_unary(&self, d: [f64; 5]) -> Self {
d[0]
}
#[inline]
fn constant_like(&self, v: f64) -> Self {
v
}
#[inline]
fn with_value(&self, v: f64) -> Self {
v
}
}
impl JetFieldConst for f64 {
#[inline]
fn from_f64(x: f64) -> Self {
x
}
}
#[derive(Clone, Copy, Debug)]
pub struct Dual2<S: JetField> {
pub v: S,
pub g: S,
pub h: S,
}
impl<S: JetFieldConst> Dual2<S> {
#[inline]
pub fn constant(v: S) -> Self {
Self {
v,
g: S::from_f64(0.0),
h: S::from_f64(0.0),
}
}
#[inline]
pub fn variable(v: S) -> Self {
Self {
v,
g: S::from_f64(1.0),
h: S::from_f64(0.0),
}
}
}
impl<S: JetField> JetField for Dual2<S> {
#[inline]
fn value(&self) -> f64 {
self.v.value()
}
#[inline]
fn add(&self, o: &Self) -> Self {
Self {
v: self.v.add(&o.v),
g: self.g.add(&o.g),
h: self.h.add(&o.h),
}
}
#[inline]
fn sub(&self, o: &Self) -> Self {
Self {
v: self.v.sub(&o.v),
g: self.g.sub(&o.g),
h: self.h.sub(&o.h),
}
}
#[inline]
fn mul(&self, o: &Self) -> Self {
Self {
v: self.v.mul(&o.v),
g: self.v.mul(&o.g).add(&self.g.mul(&o.v)),
h: self
.v
.mul(&o.h)
.add(&self.g.mul(&o.g).scale(2.0))
.add(&self.h.mul(&o.v)),
}
}
#[inline]
fn neg(&self) -> Self {
Self {
v: self.v.neg(),
g: self.g.neg(),
h: self.h.neg(),
}
}
#[inline]
fn scale(&self, s: f64) -> Self {
Self {
v: self.v.scale(s),
g: self.g.scale(s),
h: self.h.scale(s),
}
}
#[inline]
fn compose_unary(&self, d: [f64; 5]) -> Self {
let f0 = self.v.compose_unary([d[0], d[1], d[2], d[3], d[4]]);
let f1 = self.v.compose_unary([d[1], d[2], d[3], d[4], 0.0]);
let f2 = self.v.compose_unary([d[2], d[3], d[4], 0.0, 0.0]);
Self {
v: f0,
g: f1.mul(&self.g),
h: f1.mul(&self.h).add(&f2.mul(&self.g).mul(&self.g)),
}
}
#[inline]
fn constant_like(&self, v: f64) -> Self {
Self {
v: self.v.constant_like(v),
g: self.v.constant_like(0.0),
h: self.v.constant_like(0.0),
}
}
#[inline]
fn with_value(&self, v: f64) -> Self {
Self {
v: self.v.with_value(v),
g: self.g.clone(),
h: self.h.clone(),
}
}
}
impl<S: JetFieldConst> JetFieldConst for Dual2<S> {
#[inline]
fn from_f64(x: f64) -> Self {
Self::constant(S::from_f64(x))
}
}
pub type Dual22 = Dual2<Dual2<f64>>;
impl Dual22 {
#[inline]
pub fn channels(&self) -> [f64; 9] {
[
self.v.v, self.g.v, self.v.g, self.h.v, self.g.g, self.v.h, self.h.g, self.g.h,
self.h.h,
]
}
}
#[cfg(test)]
mod nested_dual_tower4_oracle_tests {
use super::*;
use crate::jet_tower::Tower4;
impl<const K: usize> JetFieldConst for Tower4<K> {
fn from_f64(x: f64) -> Self {
Tower4::constant(x)
}
}
}