use std::collections::HashMap;
use std::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use num::{One, Zero};
use crate::arithmetic_utils::{Ring, binom, multi_index_le};
use crate::plethystic::PowerSeries;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DifferentialOperator<R: Ring, const NUM_VARIABLES: usize> {
terms: HashMap<[usize; NUM_VARIABLES], PowerSeries<R, NUM_VARIABLES>>,
}
impl<R: Ring, const NUM_VARIABLES: usize> DifferentialOperator<R, NUM_VARIABLES> {
#[must_use = "The maximum order |alpha| in sum p(x1..xn) D^alpha"]
pub fn differential_order(&self) -> usize {
self.terms.keys().fold(0, |acc, d_operator| {
std::cmp::max(acc, d_operator.iter().sum())
})
}
}
pub trait NVarDifferentiable<const NUM_VARIABLES: usize> {
#[must_use = "differentiated by D^alpha"]
fn differentiate(&self, d_op: [usize; NUM_VARIABLES]) -> Self;
}
pub trait LeftMul<LHS> {
type Output;
fn left_mul(self, lhs: LHS) -> Self::Output;
}
impl<R: Ring, const NUM_VARIABLES: usize> LeftMul<PowerSeries<R, NUM_VARIABLES>>
for DifferentialOperator<R, NUM_VARIABLES>
{
type Output = Self;
fn left_mul(mut self, lhs: PowerSeries<R, NUM_VARIABLES>) -> Self::Output {
self.terms.values_mut().for_each(|z| {
*z *= lhs.clone();
});
self
}
}
impl<R: Ring, const NUM_VARIABLES: usize> NVarDifferentiable<NUM_VARIABLES>
for DifferentialOperator<R, NUM_VARIABLES>
{
fn differentiate(&self, d_op: [usize; NUM_VARIABLES]) -> Self {
let mut result = Self::zero();
for (alpha, p_alpha) in &self.terms {
for beta in multi_index_le(d_op) {
let mut d_beta_p = p_alpha.differentiate(beta);
if d_beta_p.is_zero() {
continue;
}
let c: usize = (0..NUM_VARIABLES)
.map(|i| binom(d_op[i], beta[i]))
.product();
d_beta_p.scale_by(R::natural_inclusion(c));
let gamma: [usize; NUM_VARIABLES] =
core::array::from_fn(|i| d_op[i] - beta[i] + alpha[i]);
result
.terms
.entry(gamma)
.and_modify(|e| *e += d_beta_p.clone())
.or_insert(d_beta_p);
}
}
result.terms.retain(|_, v| !v.is_zero());
result
}
}
impl<R: Ring, const NUM_VARIABLES: usize> DifferentialOperator<R, NUM_VARIABLES> {
pub fn differential_act<ActedOn>(&self, rhs: &ActedOn) -> ActedOn
where
ActedOn: NVarDifferentiable<NUM_VARIABLES>
+ AddAssign<ActedOn>
+ Zero
+ LeftMul<PowerSeries<R, NUM_VARIABLES>, Output = ActedOn>,
{
let mut to_return = ActedOn::zero();
for (diff_op, power_series_coeff) in &self.terms {
to_return += rhs
.differentiate(*diff_op)
.left_mul(power_series_coeff.clone());
}
to_return
}
}
impl<R: Ring, const NUM_VARIABLES: usize> AddAssign<Self>
for DifferentialOperator<R, NUM_VARIABLES>
{
fn add_assign(&mut self, rhs: Self) {
for (alpha, c) in rhs.terms {
self.terms
.entry(alpha)
.and_modify(|e| *e += c.clone())
.or_insert(c);
}
self.terms.retain(|_k, v| !v.is_zero());
}
}
impl<R: Ring, const NUM_VARIABLES: usize> Add<Self> for DifferentialOperator<R, NUM_VARIABLES> {
type Output = Self;
fn add(mut self, rhs: Self) -> Self::Output {
self += rhs;
self
}
}
impl<R: Ring, const NUM_VARIABLES: usize> Zero for DifferentialOperator<R, NUM_VARIABLES> {
fn zero() -> Self {
Self {
terms: HashMap::new(),
}
}
fn is_zero(&self) -> bool {
self.terms.iter().all(|(_, coeff)| coeff.is_zero())
}
}
impl<R: Ring, const NUM_VARIABLES: usize> MulAssign<Self>
for DifferentialOperator<R, NUM_VARIABLES>
{
fn mul_assign(&mut self, rhs: Self) {
let res = self.differential_act(&rhs);
*self = res;
}
}
impl<R: Ring, const NUM_VARIABLES: usize> Mul<Self> for DifferentialOperator<R, NUM_VARIABLES> {
type Output = Self;
fn mul(mut self, rhs: Self) -> Self::Output {
self *= rhs;
self
}
}
impl<R: Ring, const NUM_VARIABLES: usize> One for DifferentialOperator<R, NUM_VARIABLES> {
fn one() -> Self {
let mut terms = HashMap::with_capacity(1);
terms.insert([0usize; NUM_VARIABLES], PowerSeries::one());
Self { terms }
}
}
impl<R: Ring, const NUM_VARIABLES: usize> Neg for DifferentialOperator<R, NUM_VARIABLES> {
type Output = Self;
fn neg(mut self) -> Self::Output {
self.terms.values_mut().for_each(|z| {
*z = -z.clone();
});
self
}
}
impl<R: Ring, const NUM_VARIABLES: usize> SubAssign<Self>
for DifferentialOperator<R, NUM_VARIABLES>
{
fn sub_assign(&mut self, rhs: Self) {
for (alpha, c) in rhs.terms {
self.terms
.entry(alpha)
.and_modify(|e| *e -= c.clone())
.or_insert(-c);
}
self.terms.retain(|_k, v| !v.is_zero());
}
}
impl<R: Ring, const NUM_VARIABLES: usize> Sub<Self> for DifferentialOperator<R, NUM_VARIABLES> {
type Output = Self;
fn sub(mut self, rhs: Self) -> Self::Output {
self -= rhs;
self
}
}
#[cfg(test)]
mod tests {
use std::collections::HashMap;
use super::{DifferentialOperator, NVarDifferentiable};
use crate::plethystic::PowerSeries;
fn sample_operator() -> DifferentialOperator<i64, 2> {
DifferentialOperator {
terms: HashMap::from([
([0, 0], PowerSeries::new(HashMap::from([([2, 1], 1i64)]))), ([1, 0], PowerSeries::new(HashMap::from([([1, 0], 3i64)]))), ([0, 1], PowerSeries::new(HashMap::from([([0, 2], 1i64)]))), ]),
}
}
#[test]
fn differentiate_leibniz_with_vanishing_beta_contribution() {
let op = sample_operator();
let result = op.differentiate([1, 0]);
let expected = DifferentialOperator {
terms: HashMap::from([
([0, 0], PowerSeries::new(HashMap::from([([1, 1], 2i64)]))), (
[1, 0],
PowerSeries::new(HashMap::from([([2, 1], 1i64), ([0, 0], 3i64)])),
), ([2, 0], PowerSeries::new(HashMap::from([([1, 0], 3i64)]))), ([1, 1], PowerSeries::new(HashMap::from([([0, 2], 1i64)]))), ]),
};
assert_eq!(result, expected);
}
#[test]
fn differentiate_by_zero_alpha_is_identity() {
let op = sample_operator();
let result = op.differentiate([0, 0]);
assert_eq!(result, op);
}
#[test]
fn differentiate_univariate_beta_too_large_vanishes() {
let op: DifferentialOperator<i64, 1> = DifferentialOperator {
terms: HashMap::from([
([1], PowerSeries::new(HashMap::from([([1], 1i64)]))), ]),
};
let result = op.differentiate([2]);
let expected: DifferentialOperator<i64, 1> = DifferentialOperator {
terms: HashMap::from([
([3], PowerSeries::new(HashMap::from([([1], 1i64)]))), ([2], PowerSeries::new(HashMap::from([([0], 2i64)]))), ]),
};
assert_eq!(result, expected);
}
#[test]
fn mul_weyl_algebra_relation() {
let d_x: DifferentialOperator<i64, 1> = DifferentialOperator {
terms: HashMap::from([
([1], PowerSeries::new(HashMap::from([([0], 1i64)]))), ]),
};
let x_op: DifferentialOperator<i64, 1> = DifferentialOperator {
terms: HashMap::from([
([0], PowerSeries::new(HashMap::from([([1], 1i64)]))), ]),
};
let result = d_x * x_op;
let expected: DifferentialOperator<i64, 1> = DifferentialOperator {
terms: HashMap::from([
([1], PowerSeries::new(HashMap::from([([1], 1i64)]))), ([0], PowerSeries::new(HashMap::from([([0], 1i64)]))), ]),
};
assert_eq!(result, expected);
}
#[test]
fn mul_x_dx_squared() {
let x_dx: DifferentialOperator<i64, 1> = DifferentialOperator {
terms: HashMap::from([
([1], PowerSeries::new(HashMap::from([([1], 1i64)]))), ]),
};
let result = x_dx.clone() * x_dx;
let expected: DifferentialOperator<i64, 1> = DifferentialOperator {
terms: HashMap::from([
([2], PowerSeries::new(HashMap::from([([2], 1i64)]))), ([1], PowerSeries::new(HashMap::from([([1], 1i64)]))), ]),
};
assert_eq!(result, expected);
}
#[test]
fn mul_a_dag_a_harmonic_oscillator() {
let m = 2.0_f64;
let omega = 3.0_f64;
let alpha = (m * omega / 2.0).sqrt(); let beta = 1.0 / (2.0 * m * omega).sqrt();
let a_dag: DifferentialOperator<f64, 1> = DifferentialOperator {
terms: HashMap::from([
([0], PowerSeries::new(HashMap::from([([1], alpha)]))), ([1], PowerSeries::new(HashMap::from([([0], -beta)]))), ]),
};
let a: DifferentialOperator<f64, 1> = DifferentialOperator {
terms: HashMap::from([
([0], PowerSeries::new(HashMap::from([([1], alpha)]))), ([1], PowerSeries::new(HashMap::from([([0], beta)]))), ]),
};
let result = a_dag * a;
let expected: DifferentialOperator<f64, 1> = DifferentialOperator {
terms: HashMap::from([
([2], PowerSeries::new(HashMap::from([([0], -beta * beta)]))),
(
[0],
PowerSeries::new(HashMap::from([([2], alpha * alpha), ([0], -alpha * beta)])),
),
]),
};
assert_eq!(result, expected);
}
#[test]
fn n_zero_operator_ring_is_scalars() {
let three: DifferentialOperator<i64, 0> = DifferentialOperator {
terms: HashMap::from([([], PowerSeries::new(HashMap::from([([], 3i64)])))]),
};
let five: DifferentialOperator<i64, 0> = DifferentialOperator {
terms: HashMap::from([([], PowerSeries::new(HashMap::from([([], 5i64)])))]),
};
let result_diff = three.clone().differentiate([]);
assert_eq!(result_diff, three.clone());
let result_mul = three * five;
let expected: DifferentialOperator<i64, 0> = DifferentialOperator {
terms: HashMap::from([([], PowerSeries::new(HashMap::from([([], 15i64)])))]),
};
assert_eq!(result_mul, expected);
}
}