use ariadnetor_core::Scalar;
use ariadnetor_linalg::{LinalgError, TruncSvdParams, diagonal_scale};
use ariadnetor_mps::{Mpo, Mps, MpsOps};
use ariadnetor_tensor::{
BlockSparseLayout, BlockSparseStorage, DenseLayout, DenseStorage, Host, Sector, Storage,
StorageFor, Tensor, TensorLayout,
};
use super::heff::dmrg_2site_step;
use super::heff_block_sparse::dmrg_2site_step_block_sparse;
use super::heff_error::DmrgHeffError;
use super::solver::{DmrgScalar, LocalEigensolverParams};
use super::sweep::SweepDirection;
use ariadnetor_mps::BraketEnvs;
pub struct AbsorbedStep<St, L>
where
St: Storage + StorageFor<L>,
L: TensorLayout,
{
pub site_i: Tensor<St, L>,
pub site_ip1: Tensor<St, L>,
pub bond_dim: usize,
}
type StepDiagnostics<R> = (R, R, R, usize, bool);
type FullStepOutput<St, L, R> = Result<(AbsorbedStep<St, L>, StepDiagnostics<R>), FullStepError>;
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum FullStepError {
#[error("DMRG local 2-site step failed")]
Heff(#[source] DmrgHeffError),
#[error("DMRG S-absorb (diagonal scale) failed")]
Scale(#[source] LinalgError),
}
pub trait DmrgOps<T: Scalar>: MpsOps<T> {
fn full_step_k(
&self,
envs: &BraketEnvs<<Self as MpsOps<T>>::Storage, <Self as MpsOps<T>>::Layout>,
mpo: &Mpo<<Self as MpsOps<T>>::Storage, <Self as MpsOps<T>>::Layout>,
site: usize,
eigensolver: &LocalEigensolverParams,
trunc: &TruncSvdParams,
direction: SweepDirection,
) -> FullStepOutput<<Self as MpsOps<T>>::Storage, <Self as MpsOps<T>>::Layout, T::Real>;
}
impl<T> DmrgOps<T> for Mps<DenseStorage<T>, DenseLayout>
where
T: DmrgScalar,
T::Real: Scalar<Real = T::Real>,
{
fn full_step_k(
&self,
envs: &BraketEnvs<DenseStorage<T>, DenseLayout>,
mpo: &Mpo<DenseStorage<T>, DenseLayout>,
site: usize,
eigensolver: &LocalEigensolverParams,
trunc: &TruncSvdParams,
direction: SweepDirection,
) -> FullStepOutput<DenseStorage<T>, DenseLayout, T::Real> {
let result = dmrg_2site_step(envs, self, mpo, site, eigensolver, trunc)
.map_err(FullStepError::Heff)?;
let diagnostics = (
result.eigenvalue,
result.residual,
result.trunc_err,
result.iters,
result.converged,
);
let backend = Host::shared();
let bond_dim = result.s.shape()[0];
let (site_i, site_ip1) = match direction {
SweepDirection::LeftToRight => {
let s_vt = diagonal_scale(backend.as_ref(), &result.vt, result.s.data_slice(), 0)
.map_err(FullStepError::Scale)?;
(result.u, s_vt)
}
SweepDirection::RightToLeft => {
let u_s = diagonal_scale(backend.as_ref(), &result.u, result.s.data_slice(), 2)
.map_err(FullStepError::Scale)?;
(u_s, result.vt)
}
};
Ok((
AbsorbedStep {
site_i,
site_ip1,
bond_dim,
},
diagnostics,
))
}
}
impl<T, S> DmrgOps<T> for Mps<BlockSparseStorage<T>, BlockSparseLayout<S>>
where
T: DmrgScalar,
T::Real: Scalar<Real = T::Real>,
S: Sector,
{
fn full_step_k(
&self,
envs: &BraketEnvs<BlockSparseStorage<T>, BlockSparseLayout<S>>,
mpo: &Mpo<BlockSparseStorage<T>, BlockSparseLayout<S>>,
site: usize,
eigensolver: &LocalEigensolverParams,
trunc: &TruncSvdParams,
direction: SweepDirection,
) -> FullStepOutput<BlockSparseStorage<T>, BlockSparseLayout<S>, T::Real> {
let result = dmrg_2site_step_block_sparse(envs, self, mpo, site, eigensolver, trunc)
.map_err(FullStepError::Heff)?;
let diagnostics = (
result.eigenvalue,
result.residual,
result.trunc_err,
result.iters,
result.converged,
);
let backend = Host::shared();
let bond_dim: usize = result.s.values.iter().map(|(_, v)| v.len()).sum();
let (site_i, site_ip1) = match direction {
SweepDirection::LeftToRight => {
let s_vt = diagonal_scale(backend.as_ref(), &result.vt, &result.s, 0)
.map_err(FullStepError::Scale)?;
(result.u, s_vt)
}
SweepDirection::RightToLeft => {
let u_s = diagonal_scale(backend.as_ref(), &result.u, &result.s, 2)
.map_err(FullStepError::Scale)?;
(u_s, result.vt)
}
};
Ok((
AbsorbedStep {
site_i,
site_ip1,
bond_dim,
},
diagnostics,
))
}
}