#![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(
feature = "nightly",
feature(trivial_bounds, portable_simd, min_specialization)
)]
#[cfg(feature = "wasm_compat")]
pub extern crate nalgebra_compat as nalgebra;
#[cfg(all(feature = "latest_deps", not(feature = "wasm_compat")))]
pub extern crate nalgebra_latest as nalgebra;
#[cfg(all(feature = "bevy", feature = "wasm_compat"))]
pub extern crate bevy_reflect_compat as bevy_reflect;
#[cfg(all(feature = "bevy", feature = "latest_deps", not(feature = "wasm_compat")))]
pub extern crate bevy_reflect_latest as bevy_reflect;
extern crate alloc;
#[cfg(any(feature = "std", test))]
extern crate std;
pub mod differentiable_function;
pub mod forward_ad;
pub mod function_engine;
#[cfg(feature = "std")]
pub mod reverse_ad;
pub mod simd;
#[cfg(feature = "bevy")]
use bevy_reflect::Reflect;
#[cfg(feature = "bevy")]
pub trait MaybeReflect: Reflect {}
#[cfg(feature = "bevy")]
impl<T: Reflect> MaybeReflect for T {}
#[cfg(not(feature = "bevy"))]
pub trait MaybeReflect {}
#[cfg(not(feature = "bevy"))]
impl<T> MaybeReflect for T {}
use core::cmp::Ordering;
use core::fmt::{Debug, Display};
use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Rem, RemAssign, Sub, SubAssign};
use nalgebra::{Dim, Matrix, RawStorageMut, Scalar};
use ndarray::{ArrayBase, Dimension, OwnedRepr, ScalarOperand};
use num_traits::Signed;
use serde::de::DeserializeOwned;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{DeserializeAs, SerializeAs};
use simba::scalar::{ComplexField, RealField};
use simba::simd::{SimdComplexField, SimdRealField};
pub trait AD :
RealField +
ComplexField +
PartialOrd +
PartialEq +
Signed +
Scalar +
Clone +
Copy +
Debug +
Display +
Default +
Add<F64, Output=Self> +
AddAssign<F64> +
Mul<F64, Output=Self> +
MulAssign<F64> +
Sub<F64, Output=Self> +
SubAssign<F64> +
Div<F64, Output=Self> +
DivAssign<F64> +
Rem<F64, Output=Self> +
RemAssign<F64> +
From<f32> +
Into<f64> +
SimdRealField +
SimdComplexField +
Serialize +
DeserializeOwned +
MaybeReflect +
ScalarOperand
{
fn constant(constant: f64) -> Self;
fn to_constant(&self) -> f64;
#[inline(always)]
fn to_constant_ad(&self) -> Self {
Self::constant(self.to_constant())
}
fn ad_num_mode() -> ADNumMode;
fn ad_num_type() -> ADNumType;
fn add_scalar(arg1: f64, arg2: Self) -> Self;
fn sub_l_scalar(arg1: f64, arg2: Self) -> Self;
fn sub_r_scalar(arg1: Self, arg2: f64) -> Self;
fn mul_scalar(arg1: f64, arg2: Self) -> Self;
fn div_l_scalar(arg1: f64, arg2: Self) -> Self;
fn div_r_scalar(arg1: Self, arg2: f64) -> Self;
fn rem_l_scalar(arg1: f64, arg2: Self) -> Self;
fn rem_r_scalar(arg1: Self, arg2: f64) -> Self;
fn mul_by_nalgebra_matrix<R: Clone + Dim, C: Clone + Dim, S: Clone + RawStorageMut<Self, R, C>>(&self, other: Matrix<Self, R, C, S>) -> Matrix<Self, R, C, S>;
fn mul_by_nalgebra_matrix_ref<'a, R: Clone + Dim, C: Clone + Dim, S: Clone + RawStorageMut<Self, R, C>>(&'a self, other: &'a Matrix<Self, R, C, S>) -> Matrix<Self, R, C, S>;
fn mul_by_ndarray_matrix_ref<D: Dimension>(&self, other: &ArrayBase<OwnedRepr<Self>, D>) -> ArrayBase<OwnedRepr<Self>, D>;
fn to_other_ad_type<T2: AD>(&self) -> T2 {
T2::constant(self.to_constant())
}
}
pub trait ObjectAD {
fn to_constant(&self) -> f64;
}
#[macro_export]
macro_rules! ad_setup {
($($T: ident),*) => {
$(
ad_setup_f64!($T);
)*
}
}
ad_setup!(f64, f32);
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum ADNumMode {
Float,
ForwardAD,
#[cfg(feature = "std")]
ReverseAD,
SIMDNum,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[allow(non_camel_case_types)]
pub enum ADNumType {
F64,
F32,
#[cfg(feature = "std")]
ADR,
ADFN,
ADF,
F64XN,
#[cfg(feature = "hessian")]
HYPER_ADFN,
#[cfg(feature = "hessian")]
HYPER_ADR,
}
pub trait FloatADTrait: AD {}
impl AD for f64 {
fn constant(v: f64) -> Self {
return v;
}
fn to_constant(&self) -> f64 {
*self
}
fn ad_num_mode() -> ADNumMode {
ADNumMode::Float
}
fn ad_num_type() -> ADNumType {
ADNumType::F64
}
fn add_scalar(arg1: f64, arg2: Self) -> Self {
arg1 + arg2
}
fn sub_l_scalar(arg1: f64, arg2: Self) -> Self {
arg1 - arg2
}
fn sub_r_scalar(arg1: Self, arg2: f64) -> Self {
arg1 - arg2
}
fn mul_scalar(arg1: f64, arg2: Self) -> Self {
arg1 * arg2
}
fn div_l_scalar(arg1: f64, arg2: Self) -> Self {
arg1 / arg2
}
fn div_r_scalar(arg1: Self, arg2: f64) -> Self {
arg1 / arg2
}
fn rem_l_scalar(arg1: f64, arg2: Self) -> Self {
arg1 % arg2
}
fn rem_r_scalar(arg1: Self, arg2: f64) -> Self {
arg1 % arg2
}
fn mul_by_nalgebra_matrix<
R: Clone + Dim,
C: Clone + Dim,
S: Clone + RawStorageMut<Self, R, C>,
>(
&self,
other: Matrix<Self, R, C, S>,
) -> Matrix<Self, R, C, S> {
let mut out = other.clone();
out.iter_mut().for_each(|x| *x *= *self);
out
}
fn mul_by_nalgebra_matrix_ref<
'a,
R: Clone + Dim,
C: Clone + Dim,
S: Clone + RawStorageMut<Self, R, C>,
>(
&'a self,
other: &'a Matrix<Self, R, C, S>,
) -> Matrix<Self, R, C, S> {
let mut out = other.clone();
out.iter_mut().for_each(|x| *x *= *self);
out
}
fn mul_by_ndarray_matrix_ref<D: Dimension>(
&self,
other: &ArrayBase<OwnedRepr<Self>, D>,
) -> ArrayBase<OwnedRepr<Self>, D> {
other * *self
}
}
impl FloatADTrait for f64 {}
impl AD for f32 {
fn constant(v: f64) -> Self {
return v as f32;
}
fn to_constant(&self) -> f64 {
*self as f64
}
fn ad_num_mode() -> ADNumMode {
ADNumMode::Float
}
fn ad_num_type() -> ADNumType {
ADNumType::F32
}
fn add_scalar(arg1: f64, arg2: Self) -> Self {
arg1 as f32 + arg2
}
fn sub_l_scalar(arg1: f64, arg2: Self) -> Self {
arg1 as f32 - arg2
}
fn sub_r_scalar(arg1: Self, arg2: f64) -> Self {
arg1 - arg2 as f32
}
fn mul_scalar(arg1: f64, arg2: Self) -> Self {
arg1 as f32 * arg2
}
fn div_l_scalar(arg1: f64, arg2: Self) -> Self {
arg1 as f32 / arg2
}
fn div_r_scalar(arg1: Self, arg2: f64) -> Self {
arg1 / arg2 as f32
}
fn rem_l_scalar(arg1: f64, arg2: Self) -> Self {
arg1 as f32 % arg2
}
fn rem_r_scalar(arg1: Self, arg2: f64) -> Self {
arg1 % arg2 as f32
}
fn mul_by_nalgebra_matrix<
R: Clone + Dim,
C: Clone + Dim,
S: Clone + RawStorageMut<Self, R, C>,
>(
&self,
other: Matrix<Self, R, C, S>,
) -> Matrix<Self, R, C, S> {
let mut out = other.clone();
out.iter_mut().for_each(|x| *x *= *self);
out
}
fn mul_by_nalgebra_matrix_ref<
'a,
R: Clone + Dim,
C: Clone + Dim,
S: Clone + RawStorageMut<Self, R, C>,
>(
&'a self,
other: &'a Matrix<Self, R, C, S>,
) -> Matrix<Self, R, C, S> {
let mut out = other.clone();
out.iter_mut().for_each(|x| *x *= *self);
out
}
fn mul_by_ndarray_matrix_ref<D: Dimension>(
&self,
other: &ArrayBase<OwnedRepr<Self>, D>,
) -> ArrayBase<OwnedRepr<Self>, D> {
other * *self
}
}
impl FloatADTrait for f32 {}
#[derive(Clone, Debug, Copy)]
pub struct F64(pub f64);
impl<T: AD> Add<T> for F64 {
type Output = T;
#[inline]
fn add(self, rhs: T) -> Self::Output {
AD::add_scalar(self.0, rhs)
}
}
impl<T: AD> Mul<T> for F64 {
type Output = T;
fn mul(self, rhs: T) -> Self::Output {
AD::mul_scalar(self.0, rhs)
}
}
impl<T: AD> Sub<T> for F64 {
type Output = T;
fn sub(self, rhs: T) -> Self::Output {
AD::sub_l_scalar(self.0, rhs)
}
}
impl<T: AD> Div<T> for F64 {
type Output = T;
fn div(self, rhs: T) -> Self::Output {
AD::div_l_scalar(self.0, rhs)
}
}
impl<T: AD> Rem<T> for F64 {
type Output = T;
fn rem(self, rhs: T) -> Self::Output {
AD::rem_l_scalar(self.0, rhs)
}
}
#[macro_export]
macro_rules! ad_setup_f64 {
($T: ident) => {
impl Add<F64> for $T {
type Output = $T;
#[inline]
fn add(self, rhs: F64) -> Self::Output {
AD::add_scalar(rhs.0, self)
}
}
impl AddAssign<F64> for $T {
#[inline]
fn add_assign(&mut self, rhs: F64) {
*self = *self + rhs;
}
}
impl Mul<F64> for $T {
type Output = $T;
#[inline]
fn mul(self, rhs: F64) -> Self::Output {
AD::mul_scalar(rhs.0, self)
}
}
impl MulAssign<F64> for $T {
#[inline]
fn mul_assign(&mut self, rhs: F64) {
*self = *self * rhs;
}
}
impl Sub<F64> for $T {
type Output = $T;
#[inline]
fn sub(self, rhs: F64) -> Self::Output {
AD::sub_r_scalar(self, rhs.0)
}
}
impl SubAssign<F64> for $T {
#[inline]
fn sub_assign(&mut self, rhs: F64) {
*self = *self - rhs;
}
}
impl Div<F64> for $T {
type Output = $T;
#[inline]
fn div(self, rhs: F64) -> Self::Output {
AD::div_r_scalar(self, rhs.0)
}
}
impl DivAssign<F64> for $T {
#[inline]
fn div_assign(&mut self, rhs: F64) {
*self = *self / rhs;
}
}
impl Rem<F64> for $T {
type Output = $T;
#[inline]
fn rem(self, rhs: F64) -> Self::Output {
AD::rem_r_scalar(self, rhs.0)
}
}
impl RemAssign<F64> for $T {
#[inline]
fn rem_assign(&mut self, rhs: F64) {
*self = *self % rhs;
}
}
};
}
impl<T: AD> ObjectAD for T {
fn to_constant(&self) -> f64 {
self.to_constant()
}
}
impl PartialEq<f64> for dyn ObjectAD {
fn eq(&self, other: &f64) -> bool {
self.to_constant().eq(other)
}
}
impl PartialOrd<f64> for dyn ObjectAD {
fn partial_cmp(&self, other: &f64) -> Option<Ordering> {
self.to_constant().partial_cmp(other)
}
}
pub fn ad_custom_serialize<S, T: AD>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_f64(value.to_constant())
}
pub fn ad_custom_deserialize<'de, D, T: AD>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
let constant = f64::deserialize(deserializer)?;
Ok(T::constant(constant))
}
pub struct SerdeAD<T: AD>(pub T);
impl<T: AD> SerializeAs<T> for SerdeAD<T> {
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
ad_custom_serialize(source, serializer)
}
}
impl<'de, T: AD> DeserializeAs<'de, T> for SerdeAD<T> {
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
ad_custom_deserialize(deserializer)
}
}
pub trait ADConvertableTrait {
type ConvertableType<T: AD>;
fn convert_to_other_ad_type<T1: AD, T2: AD>(
input: &Self::ConvertableType<T1>,
) -> Self::ConvertableType<T2>;
}
impl ADConvertableTrait for () {
type ConvertableType<T: AD> = ();
fn convert_to_other_ad_type<T1: AD, T2: AD>(
_input: &Self::ConvertableType<T1>,
) -> Self::ConvertableType<T2> {
()
}
}
#[cfg(feature = "hessian")]
pub mod hyper_ad;