use std::cmp::Ordering;
use std::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use crate::ad::node::TapeNode;
use crate::ad::scalar::{InnerScalar, Scalar};
use crate::ad::tape::TapeHolder;
use super::constant::Const;
use super::dual::Dual;
mod sealed {
pub trait Sealed {}
impl<T> Sealed for super::Dual<T> {}
impl<T> Sealed for super::Const<T> {}
impl<T, L, R, O> Sealed for super::BinExpr<T, L, R, O> {}
impl<T, A, O> Sealed for super::UnExpr<T, A, O> {}
}
pub trait Expr<T>: sealed::Sealed + Clone {
fn inner_value(&self) -> T;
fn push_adj(&self, parent: &mut TapeNode<T>, adj: T);
}
pub trait BinOp<T> {
fn eval(l: T, r: T) -> T;
fn d_left(l: T, r: T) -> T;
fn d_right(l: T, r: T) -> T;
}
pub trait UnOp<T> {
fn eval(x: T) -> T;
fn deriv(x: T, v: T) -> T;
}
#[derive(Clone, Copy, Debug)]
pub struct AddOp;
impl<T: InnerScalar> BinOp<T> for AddOp {
#[inline]
fn eval(l: T, r: T) -> T {
l + r
}
#[inline]
fn d_left(_l: T, _r: T) -> T {
T::one()
}
#[inline]
fn d_right(_l: T, _r: T) -> T {
T::one()
}
}
#[derive(Clone, Copy, Debug)]
pub struct SubOp;
impl<T: InnerScalar> BinOp<T> for SubOp {
#[inline]
fn eval(l: T, r: T) -> T {
l - r
}
#[inline]
fn d_left(_l: T, _r: T) -> T {
T::one()
}
#[inline]
fn d_right(_l: T, _r: T) -> T {
T::zero() - T::one()
}
}
#[derive(Clone, Copy, Debug)]
pub struct MulOp;
impl<T: InnerScalar> BinOp<T> for MulOp {
#[inline]
fn eval(l: T, r: T) -> T {
l * r
}
#[inline]
fn d_left(_l: T, r: T) -> T {
r
}
#[inline]
fn d_right(l: T, _r: T) -> T {
l
}
}
#[derive(Clone, Copy, Debug)]
pub struct DivOp;
impl<T: InnerScalar> BinOp<T> for DivOp {
#[inline]
fn eval(l: T, r: T) -> T {
l / r
}
#[inline]
fn d_left(_l: T, r: T) -> T {
T::one() / r
}
#[inline]
fn d_right(l: T, r: T) -> T {
T::zero() - l / (r * r)
}
}
#[derive(Clone, Copy, Debug)]
pub struct PowOp;
impl<T: InnerScalar> BinOp<T> for PowOp {
#[inline]
fn eval(l: T, r: T) -> T {
l.pows(r)
}
#[inline]
fn d_left(l: T, r: T) -> T {
r * l.pows(r - T::one())
}
#[inline]
fn d_right(l: T, r: T) -> T {
l.pows(r) * l.ln()
}
}
#[derive(Clone, Copy, Debug)]
pub struct MaxOp;
impl<T: InnerScalar> BinOp<T> for MaxOp {
#[inline]
fn eval(l: T, r: T) -> T {
l.max_val(r)
}
#[inline]
fn d_left(l: T, r: T) -> T {
if l.value() > r.value() {
T::one()
} else {
T::zero()
}
}
#[inline]
fn d_right(l: T, r: T) -> T {
if r.value() > l.value() {
T::one()
} else {
T::zero()
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct MinOp;
impl<T: InnerScalar> BinOp<T> for MinOp {
#[inline]
fn eval(l: T, r: T) -> T {
l.min_val(r)
}
#[inline]
fn d_left(l: T, r: T) -> T {
if l.value() < r.value() {
T::one()
} else {
T::zero()
}
}
#[inline]
fn d_right(l: T, r: T) -> T {
if r.value() < l.value() {
T::one()
} else {
T::zero()
}
}
}
macro_rules! un_op {
($name:ident, $doc:expr, $eval:expr, $d:expr) => {
#[doc = $doc]
#[derive(Clone, Copy, Debug)]
pub struct $name;
impl<T: InnerScalar> UnOp<T> for $name {
#[inline]
fn eval(x: T) -> T {
$eval(x)
}
#[inline]
fn deriv(x: T, v: T) -> T {
$d(x, v)
}
}
};
}
un_op!(
ExpOp,
"Unary exponential operator.",
Scalar::exp,
|_x: T, v: T| v
);
un_op!(
LogOp,
"Unary natural logarithm operator.",
Scalar::ln,
|x: T, _v: T| T::one() / x
);
un_op!(
SqrtOp,
"Unary square root operator.",
Scalar::sqrt,
|_x: T, v: T| T::scalar(0.5) / v
);
un_op!(
FabsOp,
"Unary absolute value operator (alias).",
Scalar::abs,
|x: T, _v: T| if x.value() >= 0.0 {
T::one()
} else {
T::zero() - T::one()
}
);
un_op!(SinOp, "Unary sine operator.", Scalar::sin, |x: T, _v: T| x
.cos());
un_op!(
CosOp,
"Unary cosine operator.",
Scalar::cos,
|x: T, _v: T| T::zero() - x.sin()
);
un_op!(
AbsOp,
"Unary absolute value operator.",
Scalar::abs,
|x: T, _v: T| if x.value() >= 0.0 {
T::one()
} else {
T::zero() - T::one()
}
);
#[derive(Clone, Copy)]
pub struct BinExpr<T, L, R, O> {
l: L,
r: R,
val: T,
_ph: std::marker::PhantomData<O>,
}
impl<T: InnerScalar, L: Expr<T>, R: Expr<T>, O: BinOp<T>> BinExpr<T, L, R, O> {
#[inline]
pub(crate) fn new(l: L, r: R) -> Self {
let val = O::eval(l.inner_value(), r.inner_value());
Self {
l,
r,
val,
_ph: std::marker::PhantomData,
}
}
}
impl<T: InnerScalar, L: Expr<T>, R: Expr<T>, O: BinOp<T> + Clone> Expr<T> for BinExpr<T, L, R, O> {
#[inline]
fn inner_value(&self) -> T {
self.val
}
fn push_adj(&self, parent: &mut TapeNode<T>, adj: T) {
let lv = self.l.inner_value();
let rv = self.r.inner_value();
self.l.push_adj(parent, adj * O::d_left(lv, rv));
self.r.push_adj(parent, adj * O::d_right(lv, rv));
}
}
#[derive(Clone, Copy)]
pub struct UnExpr<T, A, O> {
a: A,
val: T,
_ph: std::marker::PhantomData<O>,
}
impl<T: InnerScalar, A: Expr<T>, O: UnOp<T>> UnExpr<T, A, O> {
#[inline]
pub(crate) fn new(a: A) -> Self {
let val = O::eval(a.inner_value());
Self {
a,
val,
_ph: std::marker::PhantomData,
}
}
}
impl<T: InnerScalar, A: Expr<T>, O: UnOp<T> + Clone> Expr<T> for UnExpr<T, A, O> {
#[inline]
fn inner_value(&self) -> T {
self.val
}
fn push_adj(&self, parent: &mut TapeNode<T>, adj: T) {
self.a
.push_adj(parent, adj * O::deriv(self.a.inner_value(), self.val));
}
}
pub(crate) fn flatten<T: TapeHolder + InnerScalar, E: Expr<T>>(e: &E) -> Dual<T> {
let mut node = TapeNode::default();
e.push_adj(&mut node, T::one());
let ptr_opt = T::with_tape(|tape| tape.record(node));
Dual::from_raw(e.inner_value(), ptr_opt)
}
impl<T: TapeHolder + InnerScalar> Expr<T> for Dual<T> {
#[inline]
fn inner_value(&self) -> T {
self.val()
}
fn push_adj(&self, parent: &mut TapeNode<T>, deriv: T) {
if let Some(p) = self.node_ptr() {
parent.push_child(p, deriv);
}
}
}
impl<T: InnerScalar> Expr<T> for Const<T> {
#[inline]
fn inner_value(&self) -> T {
self.0
}
#[inline]
fn push_adj(&self, _: &mut TapeNode<T>, _: T) {}
}
macro_rules! impl_bin_ops_for {
(simple $Self:ty, $T:ident) => {
impl<$T: TapeHolder + InnerScalar, Rhs: Expr<$T> + Clone> Add<Rhs> for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Self, Rhs, AddOp>;
fn add(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<$T: TapeHolder + InnerScalar> Add<f64> for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Self, Const<$T>, AddOp>;
fn add(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const($T::scalar(rhs)))
}
}
impl<$T: TapeHolder + InnerScalar, Rhs: Expr<$T> + Clone> Sub<Rhs> for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Self, Rhs, SubOp>;
fn sub(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<$T: TapeHolder + InnerScalar> Sub<f64> for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Self, Const<$T>, SubOp>;
fn sub(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const($T::scalar(rhs)))
}
}
impl<$T: TapeHolder + InnerScalar, Rhs: Expr<$T> + Clone> Mul<Rhs> for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Self, Rhs, MulOp>;
fn mul(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<$T: TapeHolder + InnerScalar> Mul<f64> for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Self, Const<$T>, MulOp>;
fn mul(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const($T::scalar(rhs)))
}
}
impl<$T: TapeHolder + InnerScalar, Rhs: Expr<$T> + Clone> Div<Rhs> for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Self, Rhs, DivOp>;
fn div(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<$T: TapeHolder + InnerScalar> Div<f64> for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Self, Const<$T>, DivOp>;
fn div(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const($T::scalar(rhs)))
}
}
impl<$T: TapeHolder + InnerScalar> Neg for $Self
where
Self: Expr<$T> + Clone,
{
type Output = BinExpr<$T, Const<$T>, Self, SubOp>;
fn neg(self) -> Self::Output {
BinExpr::new(Const($T::zero()), self)
}
}
};
}
impl_bin_ops_for!(simple Dual<T>, T);
impl_bin_ops_for!(simple Const<T>, T);
impl<T: TapeHolder + InnerScalar, L, R, O, Rhs> Add<Rhs> for BinExpr<T, L, R, O>
where
Rhs: Expr<T> + Clone,
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Rhs, AddOp>;
fn add(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<T: TapeHolder + InnerScalar, L, R, O> Add<f64> for BinExpr<T, L, R, O>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Const<T>, AddOp>;
fn add(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const(T::scalar(rhs)))
}
}
impl<T: TapeHolder + InnerScalar, L, R, O, Rhs> Sub<Rhs> for BinExpr<T, L, R, O>
where
Rhs: Expr<T> + Clone,
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Rhs, SubOp>;
fn sub(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<T: TapeHolder + InnerScalar, L, R, O> Sub<f64> for BinExpr<T, L, R, O>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Const<T>, SubOp>;
fn sub(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const(T::scalar(rhs)))
}
}
impl<T: TapeHolder + InnerScalar, L, R, O, Rhs> Mul<Rhs> for BinExpr<T, L, R, O>
where
Rhs: Expr<T> + Clone,
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Rhs, MulOp>;
fn mul(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<T: TapeHolder + InnerScalar, L, R, O> Mul<f64> for BinExpr<T, L, R, O>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Const<T>, MulOp>;
fn mul(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const(T::scalar(rhs)))
}
}
impl<T: TapeHolder + InnerScalar, L, R, O, Rhs> Div<Rhs> for BinExpr<T, L, R, O>
where
Rhs: Expr<T> + Clone,
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Rhs, DivOp>;
fn div(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<T: TapeHolder + InnerScalar, L, R, O> Div<f64> for BinExpr<T, L, R, O>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Const<T>, DivOp>;
fn div(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const(T::scalar(rhs)))
}
}
impl<T: TapeHolder + InnerScalar, L, R, O> Neg for BinExpr<T, L, R, O>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Const<T>, Self, SubOp>;
fn neg(self) -> Self::Output {
BinExpr::new(Const(T::zero()), self)
}
}
impl<T: TapeHolder + InnerScalar, A, O2, Rhs> Add<Rhs> for UnExpr<T, A, O2>
where
Rhs: Expr<T> + Clone,
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Rhs, AddOp>;
fn add(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<T: TapeHolder + InnerScalar, A, O2> Add<f64> for UnExpr<T, A, O2>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Const<T>, AddOp>;
fn add(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const(T::scalar(rhs)))
}
}
impl<T: TapeHolder + InnerScalar, A, O2, Rhs> Sub<Rhs> for UnExpr<T, A, O2>
where
Rhs: Expr<T> + Clone,
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Rhs, SubOp>;
fn sub(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<T: TapeHolder + InnerScalar, A, O2> Sub<f64> for UnExpr<T, A, O2>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Const<T>, SubOp>;
fn sub(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const(T::scalar(rhs)))
}
}
impl<T: TapeHolder + InnerScalar, A, O2, Rhs> Mul<Rhs> for UnExpr<T, A, O2>
where
Rhs: Expr<T> + Clone,
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Rhs, MulOp>;
fn mul(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<T: TapeHolder + InnerScalar, A, O2> Mul<f64> for UnExpr<T, A, O2>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Const<T>, MulOp>;
fn mul(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const(T::scalar(rhs)))
}
}
impl<T: TapeHolder + InnerScalar, A, O2, Rhs> Div<Rhs> for UnExpr<T, A, O2>
where
Rhs: Expr<T> + Clone,
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Rhs, DivOp>;
fn div(self, rhs: Rhs) -> Self::Output {
BinExpr::new(self, rhs)
}
}
impl<T: TapeHolder + InnerScalar, A, O2> Div<f64> for UnExpr<T, A, O2>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Self, Const<T>, DivOp>;
fn div(self, rhs: f64) -> Self::Output {
BinExpr::new(self, Const(T::scalar(rhs)))
}
}
impl<T: TapeHolder + InnerScalar, A, O2> Neg for UnExpr<T, A, O2>
where
Self: Expr<T> + Clone,
{
type Output = BinExpr<T, Const<T>, Self, SubOp>;
fn neg(self) -> Self::Output {
BinExpr::new(Const(T::zero()), self)
}
}
impl<T: InnerScalar, L, R, O> PartialEq for BinExpr<T, L, R, O>
where
L: Expr<T>,
R: Expr<T>,
O: BinOp<T> + Clone,
{
fn eq(&self, rhs: &Self) -> bool {
self.inner_value().value() == rhs.inner_value().value()
}
}
impl<T: InnerScalar, L, R, O> PartialOrd for BinExpr<T, L, R, O>
where
L: Expr<T>,
R: Expr<T>,
O: BinOp<T> + Clone,
{
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
self.inner_value()
.value()
.partial_cmp(&rhs.inner_value().value())
}
}
impl<T: InnerScalar, L, R, O> PartialEq<f64> for BinExpr<T, L, R, O>
where
L: Expr<T>,
R: Expr<T>,
O: BinOp<T> + Clone,
{
fn eq(&self, rhs: &f64) -> bool {
self.inner_value().value() == *rhs
}
}
impl<T: InnerScalar, L, R, O> PartialOrd<f64> for BinExpr<T, L, R, O>
where
L: Expr<T>,
R: Expr<T>,
O: BinOp<T> + Clone,
{
fn partial_cmp(&self, rhs: &f64) -> Option<Ordering> {
self.inner_value().value().partial_cmp(rhs)
}
}
impl<T: InnerScalar, A, O> PartialEq for UnExpr<T, A, O>
where
A: Expr<T>,
O: UnOp<T> + Clone,
{
fn eq(&self, rhs: &Self) -> bool {
self.inner_value().value() == rhs.inner_value().value()
}
}
impl<T: InnerScalar, A, O> PartialOrd for UnExpr<T, A, O>
where
A: Expr<T>,
O: UnOp<T> + Clone,
{
fn partial_cmp(&self, rhs: &Self) -> Option<Ordering> {
self.inner_value()
.value()
.partial_cmp(&rhs.inner_value().value())
}
}
impl<T: InnerScalar, A, O> PartialEq<f64> for UnExpr<T, A, O>
where
A: Expr<T>,
O: UnOp<T> + Clone,
{
fn eq(&self, rhs: &f64) -> bool {
self.inner_value().value() == *rhs
}
}
impl<T: InnerScalar, A, O> PartialOrd<f64> for UnExpr<T, A, O>
where
A: Expr<T>,
O: UnOp<T> + Clone,
{
fn partial_cmp(&self, rhs: &f64) -> Option<Ordering> {
self.inner_value().value().partial_cmp(rhs)
}
}
pub trait FloatExt<T: InnerScalar>: Expr<T> + Clone + Sized {
#[inline]
fn exp(self) -> UnExpr<T, Self, ExpOp> {
UnExpr::new(self)
}
#[inline]
fn ln(self) -> UnExpr<T, Self, LogOp> {
UnExpr::new(self)
}
#[inline]
fn sqrt(self) -> UnExpr<T, Self, SqrtOp> {
UnExpr::new(self)
}
#[inline]
fn sin(self) -> UnExpr<T, Self, SinOp> {
UnExpr::new(self)
}
#[inline]
fn cos(self) -> UnExpr<T, Self, CosOp> {
UnExpr::new(self)
}
#[inline]
fn abs(self) -> UnExpr<T, Self, AbsOp> {
UnExpr::new(self)
}
#[inline]
fn powf(self, p: f64) -> BinExpr<T, Self, Const<T>, PowOp> {
BinExpr::new(self, Const(T::scalar(p)))
}
#[inline]
fn pow_expr<R: Expr<T> + Clone>(self, p: R) -> BinExpr<T, Self, R, PowOp> {
BinExpr::new(self, p)
}
#[inline]
fn min<R: Expr<T> + Clone>(self, r: R) -> BinExpr<T, Self, R, MinOp> {
BinExpr::new(self, r)
}
#[inline]
fn max<R: Expr<T> + Clone>(self, r: R) -> BinExpr<T, Self, R, MaxOp> {
BinExpr::new(self, r)
}
}
impl<T: InnerScalar, E: Expr<T> + Clone> FloatExt<T> for E {}
macro_rules! impl_assign {
($Trait:ident, $func:ident, $Op:ident, $sym:tt) => {
impl<T: TapeHolder + InnerScalar, E: Expr<T> + Clone> $Trait<E> for Dual<T> {
fn $func(&mut self, rhs: E) {
*self = flatten(&(self.clone() $sym rhs));
}
}
impl<T: TapeHolder + InnerScalar> $Trait<f64> for Dual<T> {
fn $func(&mut self, rhs: f64) {
*self = flatten(&(self.clone() $sym Const(T::scalar(rhs))));
}
}
};
}
impl_assign!(AddAssign, add_assign, AddOp, +);
impl_assign!(SubAssign, sub_assign, SubOp, -);
impl_assign!(MulAssign, mul_assign, MulOp, *);
impl_assign!(DivAssign, div_assign, DivOp, /);
impl<T, L, R, O> From<BinExpr<T, L, R, O>> for Dual<T>
where
T: TapeHolder + InnerScalar,
L: Expr<T> + Clone,
R: Expr<T> + Clone,
O: BinOp<T> + Clone,
{
fn from(e: BinExpr<T, L, R, O>) -> Self {
flatten(&e)
}
}
impl<T, A, O> From<UnExpr<T, A, O>> for Dual<T>
where
T: TapeHolder + InnerScalar,
A: Expr<T> + Clone,
O: UnOp<T> + Clone,
{
fn from(e: UnExpr<T, A, O>) -> Self {
flatten(&e)
}
}
impl<T: TapeHolder + InnerScalar> From<f64> for Dual<T> {
fn from(v: f64) -> Self {
Self::new(v)
}
}
impl<T: TapeHolder + InnerScalar> From<Const<T>> for Dual<T> {
fn from(c: Const<T>) -> Self {
Self::constant(c.0)
}
}
impl<T: TapeHolder + InnerScalar> std::ops::Rem for Dual<T> {
type Output = Self;
fn rem(self, r: Self) -> Self {
Self::constant(T::scalar(self.value() % r.value()))
}
}
impl<T: TapeHolder + InnerScalar> std::ops::Rem<f64> for Dual<T> {
type Output = Self;
fn rem(self, r: f64) -> Self {
Self::constant(T::scalar(self.value() % r))
}
}
#[inline]
pub fn exp<T: InnerScalar, A: Expr<T> + Clone>(a: A) -> UnExpr<T, A, ExpOp> {
UnExpr::new(a)
}
#[inline]
pub fn log<T: InnerScalar, A: Expr<T> + Clone>(a: A) -> UnExpr<T, A, LogOp> {
UnExpr::new(a)
}
#[inline]
pub fn sqrt<T: InnerScalar, A: Expr<T> + Clone>(a: A) -> UnExpr<T, A, SqrtOp> {
UnExpr::new(a)
}
#[inline]
pub fn abs<T: InnerScalar, A: Expr<T> + Clone>(a: A) -> UnExpr<T, A, AbsOp> {
UnExpr::new(a)
}
#[inline]
pub fn fabs<T: InnerScalar, A: Expr<T> + Clone>(a: A) -> UnExpr<T, A, FabsOp> {
UnExpr::new(a)
}
#[inline]
pub fn sin<T: InnerScalar, A: Expr<T> + Clone>(a: A) -> UnExpr<T, A, SinOp> {
UnExpr::new(a)
}
#[inline]
pub fn cos<T: InnerScalar, A: Expr<T> + Clone>(a: A) -> UnExpr<T, A, CosOp> {
UnExpr::new(a)
}
#[inline]
pub fn pow<T: InnerScalar, L: Expr<T> + Clone, R: Expr<T> + Clone>(
l: L,
r: R,
) -> BinExpr<T, L, R, PowOp> {
BinExpr::new(l, r)
}
#[inline]
pub fn max<T: InnerScalar, L: Expr<T> + Clone, R: Expr<T> + Clone>(
l: L,
r: R,
) -> BinExpr<T, L, R, MaxOp> {
BinExpr::new(l, r)
}
#[inline]
pub fn min<T: InnerScalar, L: Expr<T> + Clone, R: Expr<T> + Clone>(
l: L,
r: R,
) -> BinExpr<T, L, R, MinOp> {
BinExpr::new(l, r)
}