Skip to main content

tensor4all_simplett/mpo/
vidal_mpo.rs

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