use super::functions::*;
use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct NumericalRange {
pub boundary_samples: Vec<(f64, f64)>,
pub is_normal: bool,
}
#[allow(dead_code)]
impl NumericalRange {
pub fn from_eigenvalues(eigenvalues: &[f64]) -> Self {
if eigenvalues.is_empty() {
return NumericalRange {
boundary_samples: Vec::new(),
is_normal: true,
};
}
let λ_min = eigenvalues.iter().copied().fold(f64::INFINITY, f64::min);
let λ_max = eigenvalues
.iter()
.copied()
.fold(f64::NEG_INFINITY, f64::max);
let boundary_samples = vec![(λ_min, 0.0), (λ_max, 0.0)];
NumericalRange {
boundary_samples,
is_normal: true,
}
}
pub fn numerical_radius(&self) -> f64 {
self.boundary_samples
.iter()
.map(|&(x, y)| (x * x + y * y).sqrt())
.fold(0.0f64, f64::max)
}
pub fn contains_zero(&self, tol: f64) -> bool {
if self.boundary_samples.len() >= 2 {
let xmin = self
.boundary_samples
.iter()
.map(|&(x, _)| x)
.fold(f64::INFINITY, f64::min);
let xmax = self
.boundary_samples
.iter()
.map(|&(x, _)| x)
.fold(f64::NEG_INFINITY, f64::max);
xmin - tol <= 0.0 && xmax + tol >= 0.0
} else {
false
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct BoundedPerturbation {
pub perturbation_norm: f64,
pub base_growth_bound: f64,
pub base_constant: f64,
}
#[allow(dead_code)]
impl BoundedPerturbation {
pub fn new(b_norm: f64, omega: f64, m: f64) -> Self {
BoundedPerturbation {
perturbation_norm: b_norm,
base_growth_bound: omega,
base_constant: m,
}
}
pub fn perturbed_growth_bound(&self) -> f64 {
self.base_growth_bound + self.base_constant * self.perturbation_norm
}
pub fn preserves_contractivity(&self) -> bool {
self.base_growth_bound + self.base_constant * self.perturbation_norm <= 0.0
}
}
#[derive(Debug, Clone)]
pub struct FunctionAlgebraElement {
pub values: Vec<f64>,
}
impl FunctionAlgebraElement {
pub fn from_fn<F: Fn(f64) -> f64>(f: F, n: usize) -> Self {
let values = (0..n)
.map(|i| {
let t = if n <= 1 {
0.0
} else {
i as f64 / (n - 1) as f64
};
f(t)
})
.collect();
FunctionAlgebraElement { values }
}
pub fn add(&self, other: &FunctionAlgebraElement) -> FunctionAlgebraElement {
let values = self
.values
.iter()
.zip(other.values.iter())
.map(|(a, b)| a + b)
.collect();
FunctionAlgebraElement { values }
}
pub fn multiply(&self, other: &FunctionAlgebraElement) -> FunctionAlgebraElement {
let values = self
.values
.iter()
.zip(other.values.iter())
.map(|(a, b)| a * b)
.collect();
FunctionAlgebraElement { values }
}
pub fn scale(&self, c: f64) -> FunctionAlgebraElement {
FunctionAlgebraElement {
values: self.values.iter().map(|v| v * c).collect(),
}
}
pub fn sup_norm(&self) -> f64 {
self.values.iter().map(|v| v.abs()).fold(0.0_f64, f64::max)
}
pub fn l2_norm(&self) -> f64 {
if self.values.len() <= 1 {
return self.values.first().map(|v| v.abs()).unwrap_or(0.0);
}
let n = self.values.len();
let h = 1.0 / (n - 1) as f64;
let mut integral = 0.0;
for i in 0..n - 1 {
let f0 = self.values[i] * self.values[i];
let f1 = self.values[i + 1] * self.values[i + 1];
integral += (f0 + f1) * h / 2.0;
}
integral.sqrt()
}
pub fn compose(&self, g: &FunctionAlgebraElement) -> FunctionAlgebraElement {
let n = self.values.len();
if n <= 1 {
return self.clone();
}
let values = g
.values
.iter()
.map(|&gx| {
let t = gx.clamp(0.0, 1.0) * (n - 1) as f64;
let lo = (t.floor() as usize).min(n - 2);
let hi = lo + 1;
let frac = t - lo as f64;
self.values[lo] * (1.0 - frac) + self.values[hi] * frac
})
.collect();
FunctionAlgebraElement { values }
}
pub fn one(n: usize) -> Self {
FunctionAlgebraElement {
values: vec![1.0; n],
}
}
pub fn zero_fn(n: usize) -> Self {
FunctionAlgebraElement {
values: vec![0.0; n],
}
}
}
#[derive(Debug, Clone)]
pub struct Polynomial {
pub coeffs: Vec<f64>,
}
impl Polynomial {
pub fn new(coeffs: Vec<f64>) -> Self {
Polynomial { coeffs }
}
pub fn zero() -> Self {
Polynomial { coeffs: vec![0.0] }
}
pub fn constant(c: f64) -> Self {
Polynomial { coeffs: vec![c] }
}
pub fn identity() -> Self {
Polynomial {
coeffs: vec![0.0, 1.0],
}
}
pub fn degree(&self) -> usize {
if self.coeffs.is_empty() {
return 0;
}
for i in (0..self.coeffs.len()).rev() {
if self.coeffs[i].abs() > 1e-15 {
return i;
}
}
0
}
pub fn eval(&self, x: f64) -> f64 {
if self.coeffs.is_empty() {
return 0.0;
}
let mut result = 0.0;
for c in self.coeffs.iter().rev() {
result = result * x + c;
}
result
}
pub fn add(&self, other: &Polynomial) -> Polynomial {
let max_len = self.coeffs.len().max(other.coeffs.len());
let mut result = vec![0.0; max_len];
for (i, c) in self.coeffs.iter().enumerate() {
result[i] += c;
}
for (i, c) in other.coeffs.iter().enumerate() {
result[i] += c;
}
Polynomial { coeffs: result }
}
pub fn multiply(&self, other: &Polynomial) -> Polynomial {
if self.coeffs.is_empty() || other.coeffs.is_empty() {
return Polynomial::zero();
}
let n = self.coeffs.len() + other.coeffs.len() - 1;
let mut result = vec![0.0; n];
for (i, a) in self.coeffs.iter().enumerate() {
for (j, b) in other.coeffs.iter().enumerate() {
result[i + j] += a * b;
}
}
Polynomial { coeffs: result }
}
pub fn scale(&self, s: f64) -> Polynomial {
Polynomial {
coeffs: self.coeffs.iter().map(|c| c * s).collect(),
}
}
pub fn compose(&self, other: &Polynomial) -> Polynomial {
if self.coeffs.is_empty() {
return Polynomial::zero();
}
let mut result = Polynomial::constant(*self.coeffs.last().unwrap_or(&0.0));
for c in self.coeffs.iter().rev().skip(1) {
result = result.multiply(other).add(&Polynomial::constant(*c));
}
result
}
}
#[derive(Debug, Clone)]
pub struct SpectralRadiusComputer {
pub max_iters: u32,
pub tol: f64,
}
impl SpectralRadiusComputer {
pub fn new(max_iters: u32, tol: f64) -> Self {
SpectralRadiusComputer { max_iters, tol }
}
pub fn default() -> Self {
SpectralRadiusComputer {
max_iters: 30,
tol: 1e-8,
}
}
pub fn compute(&self, mat: &SquareMatrix) -> f64 {
let mut best = f64::INFINITY;
let mut prev = f64::INFINITY;
for k in 1..=self.max_iters {
let nk = mat.pow(k).frobenius_norm();
let rk = if nk == 0.0 {
0.0
} else {
nk.powf(1.0 / k as f64)
};
if rk < best {
best = rk;
}
if (rk - prev).abs() < self.tol {
break;
}
prev = rk;
}
best
}
pub fn power_vector_method(&self, mat: &SquareMatrix, init: &[f64]) -> f64 {
assert_eq!(init.len(), mat.dim, "init must have length mat.dim");
let n = mat.dim;
let mut v: Vec<f64> = init.to_vec();
let norm0: f64 = v.iter().map(|x| x * x).sum::<f64>().sqrt();
if norm0 > 1e-15 {
for x in &mut v {
*x /= norm0;
}
}
let mut lambda = 0.0;
for _ in 0..self.max_iters {
let mut w = vec![0.0; n];
for i in 0..n {
for j in 0..n {
w[i] += mat.get(i, j) * v[j];
}
}
let dot_wv: f64 = w.iter().zip(v.iter()).map(|(a, b)| a * b).sum();
lambda = dot_wv.abs();
let nw: f64 = w.iter().map(|x| x * x).sum::<f64>().sqrt();
if nw < 1e-15 {
return 0.0;
}
for x in &mut w {
*x /= nw;
}
v = w;
}
lambda
}
}
#[derive(Debug, Clone)]
pub struct FredholmIndexCalculator {
pub rows: usize,
pub cols: usize,
pub entries: Vec<f64>,
}
impl FredholmIndexCalculator {
pub fn new(rows: usize, cols: usize, entries: Vec<f64>) -> Self {
assert_eq!(entries.len(), rows * cols, "entries must be rows*cols");
FredholmIndexCalculator {
rows,
cols,
entries,
}
}
fn get(&self, r: usize, c: usize) -> f64 {
self.entries[r * self.cols + c]
}
pub fn numerical_rank(&self, tol: f64) -> usize {
let mut mat: Vec<Vec<f64>> = (0..self.rows)
.map(|r| (0..self.cols).map(|c| self.get(r, c)).collect())
.collect();
let mut rank = 0;
let mut col = 0;
let mut row = 0;
while row < self.rows && col < self.cols {
let mut max_val = 0.0;
let mut max_row = row;
for r in row..self.rows {
if mat[r][col].abs() > max_val {
max_val = mat[r][col].abs();
max_row = r;
}
}
if max_val < tol {
col += 1;
continue;
}
mat.swap(row, max_row);
let pivot = mat[row][col];
for c in col..self.cols {
mat[row][c] /= pivot;
}
for r in 0..self.rows {
if r != row {
let factor = mat[r][col];
for c in col..self.cols {
mat[r][c] -= factor * mat[row][c];
}
}
}
rank += 1;
row += 1;
col += 1;
}
rank
}
pub fn kernel_dim(&self, tol: f64) -> usize {
self.cols - self.numerical_rank(tol)
}
pub fn cokernel_dim(&self, tol: f64) -> usize {
self.rows - self.numerical_rank(tol)
}
pub fn fredholm_index(&self, tol: f64) -> i64 {
let k = self.kernel_dim(tol) as i64;
let c = self.cokernel_dim(tol) as i64;
k - c
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SpectralMeasure {
pub eigenvalues: Vec<f64>,
pub projector_vectors: Vec<Vec<f64>>,
pub dim: usize,
}
#[allow(dead_code)]
impl SpectralMeasure {
pub fn diagonal(eigenvalues: Vec<f64>) -> Self {
let dim = eigenvalues.len();
let mut projector_vectors = Vec::new();
for i in 0..dim {
let mut v = vec![0.0f64; dim];
v[i] = 1.0;
projector_vectors.push(v);
}
SpectralMeasure {
eigenvalues,
projector_vectors,
dim,
}
}
pub fn apply_function<F: Fn(f64) -> f64>(&self, f: F) -> Vec<f64> {
self.eigenvalues.iter().map(|&λ| f(λ)).collect()
}
pub fn trace_of_function<F: Fn(f64) -> f64>(&self, f: F) -> f64 {
self.eigenvalues.iter().map(|&λ| f(λ)).sum()
}
pub fn spectral_radius(&self) -> f64 {
self.eigenvalues
.iter()
.map(|&λ| λ.abs())
.fold(0.0f64, f64::max)
}
pub fn operator_norm(&self) -> f64 {
self.spectral_radius()
}
pub fn is_positive(&self) -> bool {
self.eigenvalues.iter().all(|&λ| λ >= 0.0)
}
pub fn is_positive_definite(&self) -> bool {
self.eigenvalues.iter().all(|&λ| λ > 0.0)
}
pub fn sqrt_eigenvalues(&self) -> Option<Vec<f64>> {
if !self.is_positive() {
return None;
}
Some(self.eigenvalues.iter().map(|&λ| λ.sqrt()).collect())
}
pub fn exp_eigenvalues(&self) -> Vec<f64> {
self.eigenvalues.iter().map(|&λ| λ.exp()).collect()
}
pub fn log_eigenvalues(&self) -> Option<Vec<f64>> {
if !self.is_positive_definite() {
return None;
}
Some(self.eigenvalues.iter().map(|&λ| λ.ln()).collect())
}
}
#[derive(Debug, Clone)]
pub struct BanachAlgebraElem {
pub matrix: SquareMatrix,
pub algebra_name: String,
}
impl BanachAlgebraElem {
pub fn new(matrix: SquareMatrix, algebra_name: impl Into<String>) -> Self {
BanachAlgebraElem {
matrix,
algebra_name: algebra_name.into(),
}
}
pub fn norm(&self) -> f64 {
self.matrix.frobenius_norm()
}
pub fn spectral_radius_estimate(&self, iters: u32) -> f64 {
self.matrix.spectral_radius(iters)
}
pub fn is_invertible_2x2(&self) -> Option<bool> {
if self.matrix.dim != 2 {
return None;
}
let m = &self.matrix;
let det = m.get(0, 0) * m.get(1, 1) - m.get(0, 1) * m.get(1, 0);
Some(det.abs() > 1e-12)
}
pub fn is_in_spectrum_2x2(&self, lambda: f64) -> Option<bool> {
if self.matrix.dim != 2 {
return None;
}
let shifted = self.matrix.sub(&SquareMatrix::identity(2).scale(lambda));
let det = shifted.get(0, 0) * shifted.get(1, 1) - shifted.get(0, 1) * shifted.get(1, 0);
Some(det.abs() <= 1e-10)
}
pub fn spectrum_2x2(&self) -> Option<[(f64, f64); 2]> {
if self.matrix.dim != 2 {
return None;
}
let m = &self.matrix;
let tr = m.get(0, 0) + m.get(1, 1);
let det = m.get(0, 0) * m.get(1, 1) - m.get(0, 1) * m.get(1, 0);
let disc = tr * tr - 4.0 * det;
if disc >= 0.0 {
let s = disc.sqrt();
Some([((tr + s) / 2.0, 0.0), ((tr - s) / 2.0, 0.0)])
} else {
let s = (-disc).sqrt();
Some([(tr / 2.0, s / 2.0), (tr / 2.0, -s / 2.0)])
}
}
}
#[derive(Debug, Clone)]
pub struct OperatorSemigroup {
pub generator: SquareMatrix,
pub num_steps: u32,
}
impl OperatorSemigroup {
pub fn new(generator: SquareMatrix, num_steps: u32) -> Self {
OperatorSemigroup {
generator,
num_steps,
}
}
pub fn eval(&self, t: f64) -> SquareMatrix {
let n = self.num_steps.max(1);
let dt = t / n as f64;
let dim = self.generator.dim;
let step = SquareMatrix::identity(dim).add(&self.generator.scale(dt));
step.pow(n)
}
pub fn apply(&self, t: f64, x0: &[f64]) -> Vec<f64> {
let tt = self.eval(t);
assert_eq!(x0.len(), tt.dim);
let n = tt.dim;
let mut result = vec![0.0; n];
for i in 0..n {
for j in 0..n {
result[i] += tt.get(i, j) * x0[j];
}
}
result
}
pub fn check_semigroup_property(&self, s: f64, t: f64) -> f64 {
let t_sum = self.eval(s + t);
let t_s = self.eval(s);
let t_t = self.eval(t);
let product = t_s.mul(&t_t);
let diff = t_sum.sub(&product);
let err = diff.frobenius_norm();
let denom = t_sum.frobenius_norm();
if denom < 1e-15 {
err
} else {
err / denom
}
}
}
#[derive(Debug, Clone)]
pub struct TraceClassNorm {
pub matrix: SquareMatrix,
}
impl TraceClassNorm {
pub fn new(matrix: SquareMatrix) -> Self {
TraceClassNorm { matrix }
}
fn transpose(&self) -> SquareMatrix {
let n = self.matrix.dim;
let mut entries = vec![0.0; n * n];
for i in 0..n {
for j in 0..n {
entries[j * n + i] = self.matrix.get(i, j);
}
}
SquareMatrix { entries, dim: n }
}
fn gram_matrix(&self) -> SquareMatrix {
self.transpose().mul(&self.matrix)
}
pub fn largest_singular_value(&self, iters: u32) -> f64 {
let gram = self.gram_matrix();
let computer = SpectralRadiusComputer::new(iters, 1e-10);
let init: Vec<f64> = (0..self.matrix.dim)
.map(|i| if i == 0 { 1.0 } else { 0.0 })
.collect();
let lambda_sq = computer.power_vector_method(&gram, &init);
lambda_sq.sqrt()
}
pub fn trace_norm_2x2(&self) -> Option<f64> {
if self.matrix.dim != 2 {
return None;
}
let gram = self.gram_matrix();
let tr = gram.get(0, 0) + gram.get(1, 1);
let det = gram.get(0, 0) * gram.get(1, 1) - gram.get(0, 1) * gram.get(1, 0);
let disc = (tr * tr - 4.0 * det).max(0.0);
let ev1 = ((tr + disc.sqrt()) / 2.0).max(0.0);
let ev2 = ((tr - disc.sqrt()) / 2.0).max(0.0);
Some(ev1.sqrt() + ev2.sqrt())
}
pub fn hilbert_schmidt_norm(&self) -> f64 {
self.matrix.frobenius_norm()
}
pub fn trace_norm_estimate(&self) -> f64 {
self.matrix.frobenius_norm()
}
pub fn trace(&self) -> f64 {
self.matrix.trace()
}
pub fn lidskii_error_2x2(&self) -> Option<f64> {
if self.matrix.dim != 2 {
return None;
}
let elem = BanachAlgebraElem::new(self.matrix.clone(), "M_2");
let evs = elem.spectrum_2x2()?;
let sum_ev = evs[0].0 + evs[1].0;
Some((self.trace() - sum_ev).abs())
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ResolventData {
pub spectrum: Vec<f64>,
pub norm_bound: f64,
pub is_compact: bool,
pub is_self_adjoint: bool,
}
#[allow(dead_code)]
impl ResolventData {
pub fn new(spectrum: Vec<f64>, norm_bound: f64) -> Self {
ResolventData {
spectrum,
norm_bound,
is_compact: false,
is_self_adjoint: false,
}
}
pub fn in_resolvent_set(&self, lambda: f64, tol: f64) -> bool {
self.spectrum.iter().all(|&sv| (lambda - sv).abs() > tol)
}
pub fn resolvent_norm_estimate(&self, lambda: f64) -> Option<f64> {
if !self.is_self_adjoint {
return None;
}
let min_dist = self
.spectrum
.iter()
.map(|&spec_val| (lambda - spec_val).abs())
.fold(f64::INFINITY, f64::min);
if min_dist < 1e-14 {
None
} else {
Some(1.0 / min_dist)
}
}
pub fn spectral_gap(&self) -> Option<f64> {
if self.spectrum.len() < 2 {
return None;
}
let mut sorted = self.spectrum.clone();
sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
let gap = sorted
.windows(2)
.map(|w| w[1] - w[0])
.fold(f64::INFINITY, f64::min);
Some(gap)
}
pub fn is_invertible(&self) -> bool {
self.in_resolvent_set(0.0, 1e-14)
}
}
#[derive(Debug, Clone)]
pub struct SquareMatrix {
pub entries: Vec<f64>,
pub dim: usize,
}
impl SquareMatrix {
pub fn new(entries: Vec<f64>, dim: usize) -> Self {
assert_eq!(
entries.len(),
dim * dim,
"entries must have dim*dim elements"
);
SquareMatrix { entries, dim }
}
pub fn identity(dim: usize) -> Self {
let mut entries = vec![0.0; dim * dim];
for i in 0..dim {
entries[i * dim + i] = 1.0;
}
SquareMatrix { entries, dim }
}
pub fn zero(dim: usize) -> Self {
SquareMatrix {
entries: vec![0.0; dim * dim],
dim,
}
}
pub fn get(&self, row: usize, col: usize) -> f64 {
self.entries[row * self.dim + col]
}
pub fn set(&mut self, row: usize, col: usize, val: f64) {
self.entries[row * self.dim + col] = val;
}
pub fn add(&self, other: &SquareMatrix) -> SquareMatrix {
assert_eq!(self.dim, other.dim);
let entries: Vec<f64> = self
.entries
.iter()
.zip(other.entries.iter())
.map(|(a, b)| a + b)
.collect();
SquareMatrix {
entries,
dim: self.dim,
}
}
pub fn sub(&self, other: &SquareMatrix) -> SquareMatrix {
assert_eq!(self.dim, other.dim);
let entries: Vec<f64> = self
.entries
.iter()
.zip(other.entries.iter())
.map(|(a, b)| a - b)
.collect();
SquareMatrix {
entries,
dim: self.dim,
}
}
pub fn scale(&self, s: f64) -> SquareMatrix {
SquareMatrix {
entries: self.entries.iter().map(|x| x * s).collect(),
dim: self.dim,
}
}
pub fn mul(&self, other: &SquareMatrix) -> SquareMatrix {
assert_eq!(self.dim, other.dim);
let n = self.dim;
let mut entries = vec![0.0; n * n];
for i in 0..n {
for j in 0..n {
let mut s = 0.0;
for k in 0..n {
s += self.entries[i * n + k] * other.entries[k * n + j];
}
entries[i * n + j] = s;
}
}
SquareMatrix { entries, dim: n }
}
pub fn pow(&self, k: u32) -> SquareMatrix {
if k == 0 {
return SquareMatrix::identity(self.dim);
}
if k == 1 {
return self.clone();
}
let half = self.pow(k / 2);
let sq = half.mul(&half);
if k % 2 == 0 {
sq
} else {
sq.mul(self)
}
}
pub fn operator_norm(&self) -> f64 {
self.frobenius_norm()
}
pub fn frobenius_norm(&self) -> f64 {
self.entries.iter().map(|x| x * x).sum::<f64>().sqrt()
}
pub fn trace(&self) -> f64 {
(0..self.dim).map(|i| self.entries[i * self.dim + i]).sum()
}
pub fn poly_eval(&self, poly: &Polynomial) -> SquareMatrix {
if poly.coeffs.is_empty() {
return SquareMatrix::zero(self.dim);
}
let n = poly.coeffs.len();
let mut result = SquareMatrix::identity(self.dim).scale(poly.coeffs[n - 1]);
for i in (0..n - 1).rev() {
result = result
.mul(self)
.add(&SquareMatrix::identity(self.dim).scale(poly.coeffs[i]));
}
result
}
pub fn spectral_radius(&self, max_iter: u32) -> f64 {
let mut best = f64::INFINITY;
for k in 1..=max_iter {
let norm_k = self.pow(k).operator_norm();
let r_k = norm_k.powf(1.0 / k as f64);
if r_k < best {
best = r_k;
}
}
best
}
pub fn resolvent_2x2(&self, lambda: f64) -> Option<SquareMatrix> {
if self.dim != 2 {
return None;
}
let a = lambda - self.get(0, 0);
let b = -self.get(0, 1);
let c = -self.get(1, 0);
let d = lambda - self.get(1, 1);
let det = a * d - b * c;
if det.abs() < 1e-12 {
return None;
}
let inv_det = 1.0 / det;
Some(SquareMatrix::new(
vec![d * inv_det, -b * inv_det, -c * inv_det, a * inv_det],
2,
))
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct FiniteDimCStarAlgebra {
pub blocks: Vec<usize>,
pub name: String,
}
#[allow(dead_code)]
impl FiniteDimCStarAlgebra {
pub fn new(name: &str, blocks: Vec<usize>) -> Self {
FiniteDimCStarAlgebra {
blocks,
name: name.to_string(),
}
}
pub fn commutative(n: usize) -> Self {
FiniteDimCStarAlgebra::new(&format!("C^{n}"), vec![1; n])
}
pub fn matrix_algebra(n: usize) -> Self {
FiniteDimCStarAlgebra::new(&format!("M_{n}(C)"), vec![n])
}
pub fn dimension(&self) -> usize {
self.blocks.iter().map(|&n| n * n).sum()
}
pub fn num_irreps(&self) -> usize {
self.blocks.len()
}
pub fn is_commutative(&self) -> bool {
self.blocks.iter().all(|&n| n == 1)
}
pub fn k0_rank(&self) -> usize {
self.blocks.len()
}
pub fn is_simple(&self) -> bool {
self.blocks.len() == 1
}
pub fn normalized_trace(&self, block_idx: usize, value: f64) -> f64 {
if block_idx < self.blocks.len() {
let n = self.blocks[block_idx] as f64;
value / n
} else {
0.0
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct StrongContSemigroup {
pub generator_spectrum: Vec<f64>,
pub growth_bound: f64,
pub bound_constant: f64,
pub is_analytic: bool,
pub is_contractive: bool,
}
#[allow(dead_code)]
impl StrongContSemigroup {
pub fn new(generator_spectrum: Vec<f64>, growth_bound: f64) -> Self {
let is_contractive = growth_bound <= 0.0;
StrongContSemigroup {
generator_spectrum,
growth_bound,
bound_constant: 1.0,
is_analytic: false,
is_contractive,
}
}
pub fn norm_bound(&self, t: f64) -> f64 {
self.bound_constant * (self.growth_bound * t).exp()
}
pub fn apply_at_time(&self, t: f64, initial: &[f64]) -> Vec<f64> {
initial
.iter()
.zip(self.generator_spectrum.iter())
.map(|(&v, &λ)| v * (λ * t).exp())
.collect()
}
pub fn check_hille_yosida(&self) -> bool {
self.generator_spectrum
.iter()
.all(|&λ| λ <= self.growth_bound + 1e-10)
}
pub fn spectral_bound(&self) -> f64 {
self.generator_spectrum
.iter()
.copied()
.fold(f64::NEG_INFINITY, f64::max)
}
}