Skip to main content

tensor4all_simplett/mpo/
inverse_mpo.rs

1//! InverseMPO - Inverse form of MPO
2//!
3//! An InverseMPO stores inverse singular values for efficient local updates.
4
5use super::error::{MPOError, Result};
6use super::mpo::MPO;
7use super::types::{Tensor4, Tensor4Ops};
8use crate::traits::TTScalar;
9
10/// Inverse form of MPO
11///
12/// The inverse form is similar to Vidal form but stores inverse singular values,
13/// which is efficient for local update operations.
14#[derive(Debug, Clone)]
15pub struct InverseMPO<T: TTScalar> {
16    /// The tensors (4D tensors with normalized bond indices)
17    tensors: Vec<Tensor4<T>>,
18    /// The inverse singular values on each bond
19    inv_lambdas: Vec<Vec<f64>>,
20}
21
22impl<T: TTScalar> InverseMPO<T> {
23    /// Create an InverseMPO from an MPO
24    pub fn from_mpo(_mpo: MPO<T>) -> Result<Self> {
25        // TODO: Implement conversion to inverse form
26        Err(MPOError::InvalidOperation {
27            message: "InverseMPO::from_mpo not yet implemented".to_string(),
28        })
29    }
30
31    /// Create an InverseMPO from parts without validation
32    #[allow(dead_code)]
33    pub(crate) fn from_parts_unchecked(
34        tensors: Vec<Tensor4<T>>,
35        inv_lambdas: Vec<Vec<f64>>,
36    ) -> Self {
37        Self {
38            tensors,
39            inv_lambdas,
40        }
41    }
42
43    /// Get the number of sites
44    pub fn len(&self) -> usize {
45        self.tensors.len()
46    }
47
48    /// Check if empty
49    pub fn is_empty(&self) -> bool {
50        self.tensors.is_empty()
51    }
52
53    /// Get the tensor at position i
54    pub fn site_tensor(&self, i: usize) -> &Tensor4<T> {
55        &self.tensors[i]
56    }
57
58    /// Get mutable reference to the tensor at position i
59    pub fn site_tensor_mut(&mut self, i: usize) -> &mut Tensor4<T> {
60        &mut self.tensors[i]
61    }
62
63    /// Get all tensors
64    pub fn site_tensors(&self) -> &[Tensor4<T>] {
65        &self.tensors
66    }
67
68    /// Get the inverse Lambda vector at bond i (between sites i and i+1)
69    pub fn inv_lambda(&self, i: usize) -> &[f64] {
70        &self.inv_lambdas[i]
71    }
72
73    /// Get all inverse Lambda vectors
74    pub fn inv_lambdas(&self) -> &[Vec<f64>] {
75        &self.inv_lambdas
76    }
77
78    /// Bond dimensions
79    pub fn link_dims(&self) -> Vec<usize> {
80        self.inv_lambdas.iter().map(|l| l.len()).collect()
81    }
82
83    /// Site dimensions
84    pub fn site_dims(&self) -> Vec<(usize, usize)> {
85        self.tensors
86            .iter()
87            .map(|t| (t.site_dim_1(), t.site_dim_2()))
88            .collect()
89    }
90
91    /// Maximum bond dimension
92    pub fn rank(&self) -> usize {
93        let lds = self.link_dims();
94        if lds.is_empty() {
95            1
96        } else {
97            *lds.iter().max().unwrap_or(&1)
98        }
99    }
100
101    /// Convert to basic MPO
102    pub fn into_mpo(self) -> Result<MPO<T>> {
103        // TODO: Implement conversion back to MPO
104        Err(MPOError::InvalidOperation {
105            message: "InverseMPO::into_mpo not yet implemented".to_string(),
106        })
107    }
108}
109
110#[cfg(test)]
111mod tests;