use num_bigint::BigInt;
use num_traits::One;
use num_traits::Zero;
use serde::Deserialize;
use serde::Serialize;
use crate::symbolic::calculus::definite_integrate;
use crate::symbolic::calculus::differentiate;
use crate::symbolic::core::Expr;
use crate::symbolic::elementary::sqrt;
use crate::symbolic::simplify::is_zero;
use crate::symbolic::simplify_dag::simplify;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct HilbertSpace {
pub var: String,
pub lower_bound: Expr,
pub upper_bound: Expr,
}
impl HilbertSpace {
#[must_use]
pub fn new(
var: &str,
lower_bound: Expr,
upper_bound: Expr,
) -> Self {
Self {
var: var.to_string(),
lower_bound,
upper_bound,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct BanachSpace {
pub var: String,
pub lower_bound: Expr,
pub upper_bound: Expr,
pub p: Expr,
}
impl BanachSpace {
#[must_use]
pub fn new(
var: &str,
lower_bound: Expr,
upper_bound: Expr,
p: Expr,
) -> Self {
Self {
var: var.to_string(),
lower_bound,
upper_bound,
p,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum LinearOperator {
Identity,
Derivative(String),
Integral(Expr, String),
Multiplication(Expr),
Composition(Box<Self>, Box<Self>),
}
impl LinearOperator {
#[must_use]
pub fn apply(
&self,
expr: &Expr,
) -> Expr {
match self {
| Self::Identity => expr.clone(),
| Self::Derivative(var) => differentiate(expr, var),
| Self::Integral(lower_bound, var) => {
let x = Expr::Variable(var.clone());
definite_integrate(expr, var, lower_bound, &x)
},
| Self::Multiplication(g) => simplify(&Expr::new_mul(g.clone(), expr.clone())),
| Self::Composition(op1, op2) => op1.apply(&op2.apply(expr)),
}
}
}
#[must_use]
pub fn inner_product(
space: &HilbertSpace,
f: &Expr,
g: &Expr,
) -> Expr {
let integrand = simplify(&Expr::new_mul(f.clone(), g.clone()));
definite_integrate(
&integrand,
&space.var,
&space.lower_bound,
&space.upper_bound,
)
}
#[must_use]
pub fn norm(
space: &HilbertSpace,
f: &Expr,
) -> Expr {
let inner_product_f_f = inner_product(space, f, f);
sqrt(inner_product_f_f)
}
#[must_use]
pub fn banach_norm(
space: &BanachSpace,
f: &Expr,
) -> Expr {
let integrand = Expr::new_pow(Expr::new_abs(f.clone()), space.p.clone());
let integral = definite_integrate(
&integrand,
&space.var,
&space.lower_bound,
&space.upper_bound,
);
let one_over_p = Expr::new_div(Expr::BigInt(BigInt::one()), space.p.clone());
simplify(&Expr::new_pow(integral, one_over_p))
}
#[must_use]
pub fn are_orthogonal(
space: &HilbertSpace,
f: &Expr,
g: &Expr,
) -> bool {
let prod = simplify(&inner_product(space, f, g));
is_zero(&prod)
}
#[must_use]
pub fn project(
space: &HilbertSpace,
f: &Expr,
g: &Expr,
) -> Expr {
let inner_product_f_g = inner_product(space, f, g);
let inner_product_g_g = inner_product(space, g, g);
if is_zero(&simplify(&inner_product_g_g)) {
return Expr::BigInt(num_bigint::BigInt::zero());
}
let coefficient = simplify(&Expr::new_div(inner_product_f_g, inner_product_g_g));
simplify(&Expr::new_mul(coefficient, g.clone()))
}
#[must_use]
pub fn gram_schmidt(
space: &HilbertSpace,
basis: &[Expr],
) -> Vec<Expr> {
let mut orthogonal_basis = Vec::new();
for b in basis {
let mut v = b.clone();
for u in &orthogonal_basis {
let proj = project(space, b, u);
v = Expr::new_sub(v, proj);
}
orthogonal_basis.push(simplify(&v));
}
orthogonal_basis
}
#[must_use]
pub fn gram_schmidt_orthonormal(
space: &HilbertSpace,
basis: &[Expr],
) -> Vec<Expr> {
let orthogonal_basis = gram_schmidt(space, basis);
let mut orthonormal_basis = Vec::new();
for v in orthogonal_basis {
let n = norm(space, &v);
if is_zero(&n) {
orthonormal_basis.push(v);
} else {
orthonormal_basis.push(simplify(&Expr::new_div(v, n)));
}
}
orthonormal_basis
}