Skip to main content

TreeTN

Struct TreeTN 

Source
pub struct TreeTN<T = TensorDynLen, V = NodeIndex>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,
{ /* private fields */ }
Expand description

Tree Tensor Network structure (inspired by ITensorNetworks.jl’s TreeTensorNetwork).

Maintains a graph of tensors connected by bonds (edges). Each node stores a tensor, and edges store Connection objects that hold the bond index.

The structure uses SiteIndexNetwork to manage:

  • Topology: Graph structure (which nodes connect to which)
  • Site Space: Physical indices organized by node

§Type Parameters

  • T: Tensor type implementing TensorLike (default: TensorDynLen)
  • V: Node name type for named nodes (default: NodeIndex for backward compatibility)

§Construction

  • TreeTN::new(): Create an empty network, then use add_tensor() and connect() to build.
  • TreeTN::from_tensors(tensors, node_names): Create from tensors with auto-connection by matching index IDs.

§Examples

Build a 2-node chain manually and verify node count:

use tensor4all_treetn::TreeTN;
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};

// Create site and bond indices
let s0 = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(3);
let s1 = DynIndex::new_dyn(2);

// Build tensors
let t0 = TensorDynLen::from_dense(
    vec![s0.clone(), bond.clone()],
    vec![1.0_f64, 2.0, 3.0, 4.0, 5.0, 6.0],
).unwrap();
let t1 = TensorDynLen::from_dense(
    vec![bond.clone(), s1.clone()],
    vec![1.0_f64, 2.0, 3.0, 4.0, 5.0, 6.0],
).unwrap();

// Use from_tensors (auto-connects nodes sharing the same index ID)
let tn = TreeTN::<_, String>::from_tensors(
    vec![t0, t1],
    vec!["A".to_string(), "B".to_string()],
).unwrap();

assert_eq!(tn.node_count(), 2);
assert_eq!(tn.edge_count(), 1);

Implementations§

Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source

pub fn reindex_site_space_like(&self, template: &Self) -> Result<Self>
where V: Ord, T::Index: Clone, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Reindex this TreeTN’s site space to match a template network.

The topology must match, and each corresponding node must carry the same number of site indices with the same dimensions. Site indices are paired node-by-node after sorting by (dim, id) for deterministic matching.

§Arguments
  • template - Reference TreeTN whose site index IDs should be adopted
§Returns

A new TreeTN with the same tensor data as self, but site index IDs rewritten to match template.

§Errors

Returns an error if the two networks have different topologies or incompatible site-space dimensions on any node.

§Examples
use tensor4all_core::{DynIndex, TensorDynLen};
use tensor4all_treetn::TreeTN;

let state_a = make_chain(DynIndex::new_dyn(2), DynIndex::new_dyn(2));
let state_b = make_chain(DynIndex::new_dyn(2), DynIndex::new_dyn(2));

let aligned = state_b.reindex_site_space_like(&state_a).unwrap();
assert!(aligned.share_equivalent_site_index_network(&state_a));
Source

pub fn add_aligned(&self, other: &Self) -> Result<Self>
where V: Ord, T::Index: Clone, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Add two TreeTNs after aligning the second operand’s site index IDs to the first.

This is useful when two states share the same topology and site dimensions but were constructed with different site index IDs.

§Arguments
  • other - The other TreeTN to align and add
§Returns

The direct-sum addition result with site IDs matching self.

§Examples
use tensor4all_core::{DynIndex, TensorDynLen};
use tensor4all_treetn::TreeTN;

let state_a = make_chain(DynIndex::new_dyn(2), DynIndex::new_dyn(2));
let state_b = make_chain(DynIndex::new_dyn(2), DynIndex::new_dyn(2));

let sum = state_a.add_aligned(&state_b).unwrap();
assert_eq!(sum.node_count(), 2);
assert!(sum.share_equivalent_site_index_network(&state_a));
Source

pub fn compute_merged_bond_indices( &self, other: &Self, ) -> Result<HashMap<(V, V), MergedBondInfo<T::Index>>>
where V: Ord,

Compute merged bond indices for direct-sum addition.

For each edge in the network, compute the merged bond information containing dimensions from both networks and a new merged index.

§Arguments
  • other - The other TreeTN to compute merged bonds with
§Returns

A HashMap mapping edge keys (node_name_pair in canonical order) to MergedBondInfo.

§Errors

Returns an error if:

  • Networks have incompatible topologies
  • Bond indices cannot be found
Source

pub fn add(&self, other: &Self) -> Result<Self>
where V: Ord, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Add two TreeTNs using direct-sum construction.

This creates a new TreeTN where each tensor is the direct sum of the corresponding tensors from self and other, with bond dimensions merged. The two networks must share the same topology and the same site index IDs. Use add_aligned if site index IDs differ.

§Arguments
  • other - The other TreeTN to add
§Returns

A new TreeTN representing the sum.

§Errors

Returns an error if the networks have incompatible structures.

§Examples
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};
use tensor4all_treetn::TreeTN;

let s = DynIndex::new_dyn(2);
let t = TensorDynLen::from_dense(vec![s.clone()], vec![1.0_f64, 2.0]).unwrap();
let tn = TreeTN::<_, usize>::from_tensors(vec![t], vec![0]).unwrap();

// Adding a single-node network to itself doubles the values
let sum = tn.add(&tn).unwrap();
let dense = sum.to_dense().unwrap();
let expected = TensorDynLen::from_dense(vec![s], vec![2.0, 4.0]).unwrap();
assert!((&dense - &expected).maxabs() < 1e-12);
Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source

pub fn canonicalize( self, canonical_region: impl IntoIterator<Item = V>, options: CanonicalizationOptions, ) -> Result<Self>

Canonicalize the network towards the specified center using options.

This is the recommended unified API for canonicalization. It accepts:

§Behavior
  • If options.force is false (default):
    • Already at target with same form: returns unchanged (no-op)
    • Different form: returns an error (use options.force() to override)
  • If options.force is true:
    • Always performs full canonicalization
§Examples
use tensor4all_treetn::{TreeTN, CanonicalizationOptions};
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};

let s0 = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(3);
let s1 = DynIndex::new_dyn(2);

let t0 = TensorDynLen::from_dense(
    vec![s0.clone(), bond.clone()],
    vec![1.0_f64, 2.0, 3.0, 4.0, 5.0, 6.0],
).unwrap();
let t1 = TensorDynLen::from_dense(
    vec![bond.clone(), s1.clone()],
    vec![1.0_f64, 2.0, 3.0, 4.0, 5.0, 6.0],
).unwrap();

let tn = TreeTN::<_, String>::from_tensors(
    vec![t0, t1],
    vec!["A".to_string(), "B".to_string()],
).unwrap();

// Canonicalize towards node "A"
let tn = tn.canonicalize(["A".to_string()], CanonicalizationOptions::default()).unwrap();
assert!(tn.is_canonicalized());
Source

pub fn canonicalize_mut( &mut self, canonical_region: impl IntoIterator<Item = V>, options: CanonicalizationOptions, ) -> Result<()>
where Self: Default,

Canonicalize the network in-place towards the specified center using options.

This is the &mut self version of Self::canonicalize.

Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source

pub fn sim_internal_inds(&self) -> Self

Create a copy with all internal (link/bond) indices replaced by fresh IDs.

External (site/physical) indices remain unchanged. This is useful when contracting two TreeTNs that might have overlapping internal index IDs.

§Returns

A new TreeTN with all bond indices replaced by sim indices (same dimension, new unique ID).

Source

pub fn contract_to_tensor(&self) -> Result<T>
where V: Ord,

Contract the TreeTN to a single tensor.

This method contracts all tensors in the network into a single tensor containing all physical indices. The contraction is performed using an edge-based order (post-order DFS edges towards root), processing each edge in sequence and using Connection information to identify which indices to contract.

The result has only site (physical) indices; all bond indices are summed out. See also to_dense, which is an alias for this method.

§Returns

A single tensor representing the full contraction of the network.

§Errors

Returns an error if:

  • The network is empty
  • The graph is not a valid tree
  • Tensor contraction fails
§Examples
use tensor4all_treetn::TreeTN;
use tensor4all_core::{DynIndex, TensorDynLen, TensorIndex, TensorLike};

let s0 = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(2);
let s1 = DynIndex::new_dyn(2);

let t0 = TensorDynLen::from_dense(
    vec![s0.clone(), bond.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0],
).unwrap();
let t1 = TensorDynLen::from_dense(
    vec![bond, s1.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0],
).unwrap();

let tn = TreeTN::<_, usize>::from_tensors(vec![t0, t1], vec![0, 1]).unwrap();
let dense = tn.contract_to_tensor().unwrap();

// Result has only site indices
assert_eq!(dense.num_external_indices(), 2);
Source

pub fn contract_zipup( &self, other: &Self, center: &V, svd_policy: Option<SvdTruncationPolicy>, max_rank: Option<usize>, ) -> Result<Self>
where V: Ord, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Contract two TreeTNs with the same topology using the zip-up algorithm.

The zip-up algorithm traverses from leaves towards the center, contracting corresponding nodes from both networks and optionally truncating at each step.

§Algorithm
  1. Replace internal (bond) indices of both networks with fresh IDs to avoid collision
  2. Traverse from leaves towards center
  3. At each edge (child → parent):
    • Contract the child tensors from both networks (along their shared site indices)
    • Factorize, keeping site indices + parent bond on left (canonical form)
    • Store left factor as child tensor in result
    • Contract right factor into parent tensor
  4. Contract the final center tensors
§Arguments
  • other - The other TreeTN to contract with (must have same topology)
  • center - The center node name towards which to contract
  • svd_policy - Optional SVD truncation policy
  • max_rank - Optional maximum bond dimension
§Returns

The contracted TreeTN result, or an error if topologies don’t match or contraction fails.

Source

pub fn contract_zipup_with( &self, other: &Self, center: &V, form: CanonicalForm, svd_policy: Option<SvdTruncationPolicy>, max_rank: Option<usize>, ) -> Result<Self>
where V: Ord, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Contract two TreeTNs with the same topology using the zip-up algorithm with a specified form.

See contract_zipup for details.

Source

pub fn contract_zipup_tree_accumulated( &self, other: &Self, center: &V, form: CanonicalForm, svd_policy: Option<SvdTruncationPolicy>, max_rank: Option<usize>, ) -> Result<Self>
where V: Ord, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Contract two TreeTNs using zip-up algorithm with accumulated intermediate tensors.

This is an improved version of zip-up contraction that maintains intermediate tensors (environment tensors) as it processes from leaves towards the root, similar to ITensors.jl’s MPO zip-up algorithm.

§Algorithm
  1. Process leaves: contract A[leaf] * B[leaf], factorize, store R at parent
  2. Process internal nodes: contract [R_accumulated..., A[node], B[node]], factorize, store R_new at parent
  3. Process root: contract [R_list..., A[root], B[root]], store as final result
  4. Set canonical center
§Arguments
  • other - The other TreeTN to contract with (must have same topology)
  • center - The center node name towards which to contract
  • form - Canonical form (Unitary/LU/CI)
  • svd_policy - Optional SVD truncation policy
  • max_rank - Optional maximum bond dimension
§Returns

The contracted TreeTN result, or an error if topologies don’t match or contraction fails.

Source

pub fn contract_naive(&self, other: &Self) -> Result<T>
where V: Ord, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Contract two TreeTNs using naive full contraction.

This is a reference implementation that:

  1. Replaces internal indices with fresh IDs (sim_internal_inds)
  2. Converts both TreeTNs to full tensors
  3. Contracts along common site indices

The result is a single tensor, not a TreeTN. This is useful for:

  • Testing correctness of more sophisticated algorithms like contract_zipup
  • Computing exact results for small networks
§Arguments
  • other - The other TreeTN to contract with (must have same topology)
§Returns

A tensor representing the contracted result.

§Note

This method is O(exp(n)) in both time and memory where n is the number of nodes. Use contract_zipup for efficient contraction of large networks.

Source

pub fn validate_ortho_consistency(&self) -> Result<()>

Validate that canonical_region and edge ortho_towards are consistent.

Rules:

  • If canonical_region is empty (not canonicalized), all indices must have ortho_towards == None.
  • If canonical_region is non-empty:
    • It must form a connected subtree
    • All edges from outside the center region must have ortho_towards pointing towards the center
    • Edges entirely inside the center region may have ortho_towards == None
Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source

pub fn extract_subtree(&self, node_names: &[V]) -> Result<Self>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync, V: Ord,

Extract a sub-tree from this TreeTN.

Creates a new TreeTN containing only the specified nodes and their connecting edges. Tensors are cloned into the new TreeTN.

§Arguments
  • node_names - The names of nodes to include in the sub-tree
§Returns

A new TreeTN containing the specified sub-tree, or an error if:

  • Any specified node doesn’t exist
  • The specified nodes don’t form a connected subtree
§Notes
  • Bond indices between included nodes are preserved
  • Bond indices to excluded nodes become external (site) indices in the sub-tree
  • ortho_towards directions are copied for edges within the sub-tree
  • canonical_region is intersected with the extracted nodes
Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source

pub fn replace_subtree( &mut self, node_names: &[V], replacement: &Self, ) -> Result<()>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync, V: Clone + Hash + Eq + Ord + Send + Sync + Debug,

Replace a sub-tree with another TreeTN of the same topology.

This method replaces the tensors and ortho_towards directions for a subset of nodes with those from another TreeTN. The replacement TreeTN must have the same topology (nodes and edges) as the sub-tree being replaced.

§Arguments
  • node_names - The names of nodes to replace
  • replacement - The TreeTN to use as replacement
§Returns

Ok(()) if the replacement succeeds, or an error if:

  • Any specified node doesn’t exist
  • The replacement doesn’t have the same topology as the extracted sub-tree
  • Tensor replacement fails
§Notes
  • The replacement TreeTN must have the same nodes, edges, and site indices
  • Bond dimensions may differ (this is the typical use case for truncation)
  • ortho_towards may differ (will be copied from replacement)
  • The original TreeTN is modified in-place
Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Ord + Send + Sync + Debug,

Source

pub fn log_norm(&mut self) -> Result<f64>

Compute log(||TreeTN||_F), the log of the Frobenius norm.

Uses canonicalization to avoid numerical overflow: when canonicalized to a single site with Unitary form, the Frobenius norm of the whole network equals the norm of the center tensor.

§Note

This method is mutable because it may need to canonicalize the network to a single Unitary center. Use log_norm (without canonicalization) if you already have a properly canonicalized network.

§Returns

The natural logarithm of the Frobenius norm.

§Errors

Returns an error if:

  • The network is empty
  • Canonicalization fails
§Examples
use tensor4all_treetn::TreeTN;
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};

let s = DynIndex::new_dyn(2);
let t = TensorDynLen::from_dense(vec![s], vec![3.0_f64, 4.0]).unwrap();
let mut tn = TreeTN::<_, usize>::from_tensors(vec![t], vec![0]).unwrap();

// log(||[3, 4]||) = log(5)
let ln = tn.log_norm().unwrap();
assert!((ln - 5.0_f64.ln()).abs() < 1e-10);
Source

pub fn norm(&mut self) -> Result<f64>

Compute the Frobenius norm of the TreeTN.

Uses log_norm internally: norm = exp(log_norm).

§Note

This method is mutable because it may need to canonicalize the network.

§Errors

Returns an error if the network is empty or canonicalization fails.

§Examples
use tensor4all_treetn::TreeTN;
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};

// Single-node TreeTN with tensor [1, 0, 0, 1] (identity 2x2)
let s0 = DynIndex::new_dyn(2);
let s1 = DynIndex::new_dyn(2);
let t = TensorDynLen::from_dense(
    vec![s0.clone(), s1.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0],
).unwrap();

let mut tn = TreeTN::<_, String>::from_tensors(
    vec![t],
    vec!["A".to_string()],
).unwrap();

// Frobenius norm of [[1,0],[0,1]] = sqrt(2)
let n = tn.norm().unwrap();
assert!((n - 2.0_f64.sqrt()).abs() < 1e-10);
Source

pub fn norm_squared(&mut self) -> Result<f64>

Compute the squared Frobenius norm of the TreeTN.

Returns ||self||^2 = norm()^2.

§Note

This method is mutable because it may need to canonicalize the network.

§Errors

Returns an error if the network is empty or canonicalization fails.

§Examples
use tensor4all_treetn::TreeTN;
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};

let s = DynIndex::new_dyn(2);
let t = TensorDynLen::from_dense(vec![s], vec![3.0_f64, 4.0]).unwrap();
let mut tn = TreeTN::<_, usize>::from_tensors(vec![t], vec![0]).unwrap();

// ||[3, 4]||^2 = 9 + 16 = 25
let nsq = tn.norm_squared().unwrap();
assert!((nsq - 25.0).abs() < 1e-10);
Source

pub fn scale(&mut self, scalar: AnyScalar) -> Result<()>

Scale the tensor network by a complex scalar.

This multiplies a single node tensor, chosen deterministically as the minimum-named node, so the represented state is scaled once rather than applying scalar^n across all nodes.

Scaling a non-center tensor generally invalidates any existing canonicalization metadata, so this method clears the cached canonical region and orthogonality directions after updating the tensor.

§Arguments
  • scalar - Scalar multiplier applied to the represented tensor network
§Returns

Ok(()) after the selected node tensor has been updated in place

§Errors

Returns an error if the TreeTN is empty or the selected node/tensor cannot be found

§Examples
use tensor4all_core::{AnyScalar, DynIndex, TensorDynLen, TensorIndex, TensorLike};
use tensor4all_treetn::TreeTN;

let s = DynIndex::new_dyn(2);
let t = TensorDynLen::from_dense(vec![s], vec![1.0_f64, -2.0]).unwrap();
let mut tn = TreeTN::<_, usize>::from_tensors(vec![t], vec![0]).unwrap();

tn.scale(AnyScalar::new_real(2.0)).unwrap();

let dense = tn.to_dense().unwrap();
let expected = TensorDynLen::from_dense(
    dense.external_indices(),
    vec![2.0_f64, -4.0],
).unwrap();
assert!((&dense - &expected).maxabs() < 1e-12);
Source

pub fn inner(&self, other: &Self) -> Result<AnyScalar>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Compute the inner product of two TreeTNs.

Computes <self | other> = sum over all indices of conj(self) * other.

Both TreeTNs must have the same site indices (same IDs). Link indices may differ between the two TreeTNs.

§Algorithm
  1. Replace link indices in other with fresh IDs to avoid collision.
  2. At each node, contract conj(self_tensor) * other_tensor pairwise.
  3. Sweep from leaves to root, contracting the environment.

This is equivalent to contracting the entire network conj(self) * other into a scalar.

§Errors

Returns an error if the networks have incompatible topologies.

§Examples
use tensor4all_treetn::TreeTN;
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};

let s = DynIndex::new_dyn(2);
let t = TensorDynLen::from_dense(vec![s], vec![3.0_f64, 4.0]).unwrap();
let tn = TreeTN::<_, usize>::from_tensors(vec![t], vec![0]).unwrap();

// <v|v> = 3^2 + 4^2 = 25
let ip = tn.inner(&tn).unwrap();
assert!((ip.real() - 25.0).abs() < 1e-10);
Source

pub fn to_dense(&self) -> Result<T>

Convert the TreeTN to a single dense tensor.

This contracts all tensors in the network along their link/bond indices, producing a single tensor with only site (physical) indices.

This is an alias for contract_to_tensor().

§Warning

This operation can be very expensive for large networks, as the result size grows exponentially with the number of sites.

§Errors

Returns an error if the network is empty or contraction fails.

§Examples
use tensor4all_treetn::TreeTN;
use tensor4all_core::{DynIndex, TensorDynLen, TensorIndex, TensorLike};

// Build a 2-node chain
let s0 = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(2);
let s1 = DynIndex::new_dyn(2);

// Identity matrices
let t0 = TensorDynLen::from_dense(
    vec![s0.clone(), bond.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0],
).unwrap();
let t1 = TensorDynLen::from_dense(
    vec![bond.clone(), s1.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0],
).unwrap();

let tn = TreeTN::<_, String>::from_tensors(
    vec![t0, t1],
    vec!["A".to_string(), "B".to_string()],
).unwrap();

// Contract to a single dense tensor over site indices s0 and s1
let dense = tn.to_dense().unwrap();
// Result is rank-2 (two site indices s0 and s1)
assert_eq!(dense.num_external_indices(), 2);
Source

pub fn all_site_index_ids( &self, ) -> Result<(Vec<<T::Index as IndexLike>::Id>, Vec<V>)>
where V: Clone, <T::Index as IndexLike>::Id: Clone,

Returns all site index IDs and their owning vertex names.

Returns (index_ids, vertex_names) where index_ids[i] belongs to vertex vertex_names[i]. Order is unspecified but consistent between the two vectors.

For evaluate(), pass index_ids and arrange values in the same order.

Source

pub fn evaluate( &self, index_ids: &[<T::Index as IndexLike>::Id], values: ColMajorArrayRef<'_, usize>, ) -> Result<Vec<AnyScalar>>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Evaluate the TreeTN at multiple multi-indices (batch).

§Arguments
  • index_ids - Identifies each site index by its ID (from all_site_index_ids()). Must enumerate every site index exactly once.
  • values - Column-major array of shape [n_indices, n_points]. values.get(&[i, p]) is the value of index_ids[i] at point p.
§Returns

A Vec<AnyScalar> of length n_points.

§Errors

Returns an error if:

  • The network is empty
  • values shape is inconsistent with index_ids
  • An index ID is unknown
  • Index values are out of bounds
  • Contraction fails
Source

pub fn all_site_indices(&self) -> Result<(Vec<T::Index>, Vec<V>)>
where V: Clone, T::Index: Clone,

Returns all site indices and their owning vertex names.

Returns (indices, vertex_names) where indices[i] belongs to vertex vertex_names[i]. Order is unspecified but consistent between the two vectors.

This is the Index-based counterpart of all_site_index_ids(), returning full Index objects instead of raw IDs.

§Errors

Returns an error if a node’s site space cannot be found.

§Examples
use tensor4all_core::{DynIndex, IndexLike, TensorDynLen, TensorLike};
use tensor4all_treetn::TreeTN;

let s0 = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(3);
let s1 = DynIndex::new_dyn(2);
let t0 = TensorDynLen::from_dense(
    vec![s0.clone(), bond.clone()], vec![1.0, 0.0, 0.0, 0.0, 1.0, 0.0],
).unwrap();
let t1 = TensorDynLen::from_dense(
    vec![bond.clone(), s1.clone()], vec![1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
).unwrap();
let tn = TreeTN::<TensorDynLen, usize>::from_tensors(vec![t0, t1], vec![0, 1]).unwrap();

let (indices, vertices) = tn.all_site_indices().unwrap();
assert_eq!(indices.len(), 2);
assert_eq!(vertices.len(), 2);

// The returned indices contain both s0 and s1
let id_set: std::collections::HashSet<_> = indices.iter().map(|i| *i.id()).collect();
assert!(id_set.contains(s0.id()));
assert!(id_set.contains(s1.id()));
Source

pub fn evaluate_at( &self, indices: &[T::Index], values: ColMajorArrayRef<'_, usize>, ) -> Result<Vec<AnyScalar>>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Evaluate the TreeTN at multiple multi-indices (batch), using Index objects instead of raw IDs.

This is a convenience wrapper around evaluate() that accepts &[T::Index] directly, extracting the IDs internally.

§Arguments
  • indices - Identifies each site index by its Index object (e.g. from all_site_indices()). Must enumerate every site index exactly once.
  • values - Column-major array of shape [n_indices, n_points]. values.get(&[i, p]) is the value of indices[i] at point p.
§Returns

A Vec<AnyScalar> of length n_points.

§Errors

Returns an error if the underlying evaluate() call fails (see its documentation for details).

§Examples
use tensor4all_core::{ColMajorArrayRef, DynIndex, IndexLike, TensorDynLen, TensorLike};
use tensor4all_treetn::TreeTN;

let s0 = DynIndex::new_dyn(3);
let t0 = TensorDynLen::from_dense(vec![s0.clone()], vec![10.0, 20.0, 30.0]).unwrap();
let tn = TreeTN::<TensorDynLen, usize>::from_tensors(vec![t0], vec![0]).unwrap();

let (indices, _vertices) = tn.all_site_indices().unwrap();

// Evaluate at index value 2
let data = [2usize];
let shape = [indices.len(), 1];
let values = ColMajorArrayRef::new(&data, &shape);
let result = tn.evaluate_at(&indices, values).unwrap();
assert!((result[0].real() - 30.0).abs() < 1e-10);
Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Ord + Send + Sync + Debug,

Source

pub fn restructure_to<TargetV>( &self, target: &SiteIndexNetwork<TargetV, T::Index>, options: &RestructureOptions, ) -> Result<TreeTN<T, TargetV>>
where TargetV: Clone + Hash + Eq + Ord + Send + Sync + Debug, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Restructure this TreeTN to match a target site-index network.

This is the plan-first public entry point for Issue #423 B2a.

The current staged implementation already handles:

  • fuse-only restructures, where each current node maps to exactly one target node;
  • split-only restructures, where each target node maps to exactly one current node;
  • swap-only restructures, where the current and target topologies are tree-isomorphic and only the site assignments differ;
  • conservative path-based swap-then-fuse restructures, where the current nodes already map uniquely to target nodes but their target groups must be rearranged into contiguous path blocks before fusing;
  • conservative mixed split-then-fuse restructures, where each current node has at most one cross-node target fragment that must retain the original external bonds.

Unsupported patterns are reported explicitly. In particular, mixed cases that require both split planning and a subsequent move/swap phase may still remain intentionally staged behind placeholder errors while the pure planner is expanded.

Related types:

§Arguments
  • target - Desired final topology and site grouping.
  • options - Phase-specific options for split, transport, and optional final truncation.
§Returns

A new TreeTN with the target node naming and target site-index network.

§Errors

Returns an error when the target is structurally incompatible with the current network, or when the requested restructure still needs the staged planner paths for mixed split/move/fuse execution.

§Examples
use std::collections::HashSet;

use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};
use tensor4all_treetn::{RestructureOptions, SiteIndexNetwork, TreeTN};

let left = DynIndex::new_dyn(2);
let right = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(1);
let t0 = TensorDynLen::from_dense(vec![left.clone(), bond.clone()], vec![1.0, 0.0])?;
let t1 = TensorDynLen::from_dense(vec![bond, right.clone()], vec![1.0, 0.0])?;
let treetn = TreeTN::<TensorDynLen, String>::from_tensors(
    vec![t0, t1],
    vec!["A".to_string(), "B".to_string()],
)?;

let mut target: SiteIndexNetwork<String, DynIndex> = SiteIndexNetwork::new();
assert!(target
    .add_node("AB".to_string(), HashSet::from([left.clone(), right.clone()]))
    .is_ok());

let result = treetn.restructure_to(&target, &RestructureOptions::default())?;

assert_eq!(result.node_count(), 1);
let dense = result.contract_to_tensor()?;
let expected = treetn.contract_to_tensor()?;
assert!((&dense - &expected).maxabs() < 1e-12);
Source§

impl<V> TreeTN<TensorDynLen, V>
where V: Clone + Hash + Eq + Ord + Send + Sync + Debug, <DynIndex as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Source

pub fn replace_site_index_with_indices( &self, old_index: &DynIndex, new_indices: &[DynIndex], order: LinearizationOrder, ) -> Result<Self>

Replace one site index with multiple site indices using an exact reshape.

This is a TreeTN-level wrapper around [TensorDynLen::unfuse_index]. It updates the owning node tensor and the site-index metadata, without introducing any approximation.

§Examples
use tensor4all_core::{DynIndex, LinearizationOrder, TensorDynLen};
use tensor4all_treetn::TreeTN;

let fused = DynIndex::new_dyn(4);
let tensor = TensorDynLen::from_dense(vec![fused.clone()], vec![1.0, 2.0, 3.0, 4.0]).unwrap();
let tn = TreeTN::<TensorDynLen, usize>::from_tensors(vec![tensor], vec![0]).unwrap();
let left = DynIndex::new_dyn(2);
let right = DynIndex::new_dyn(2);

let unfused = tn
    .replace_site_index_with_indices(
        &fused,
        &[left.clone(), right.clone()],
        LinearizationOrder::ColumnMajor,
    )
    .unwrap();

let dense = unfused.contract_to_tensor().unwrap();
let expected = TensorDynLen::from_dense(vec![left, right], vec![1.0, 2.0, 3.0, 4.0]).unwrap();
assert!((&dense - &expected).maxabs() < 1.0e-12);
Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync, V: Clone + Hash + Eq + Ord + Send + Sync + Debug,

Source

pub fn fuse_to<TargetV>( &self, target: &SiteIndexNetwork<TargetV, T::Index>, ) -> Result<TreeTN<T, TargetV>>
where TargetV: Clone + Hash + Eq + Ord + Send + Sync + Debug,

Fuse (merge) adjacent nodes to match the target structure.

This operation contracts adjacent nodes that should be merged according to the target SiteIndexNetwork. The target structure must be a “coarsening” of the current structure: each target node should contain the site indices of one or more adjacent current nodes.

§Algorithm
  1. Compare current structure with target structure
  2. Map each current node to its target node (by matching site indices)
  3. For each group of current nodes mapping to the same target node:
    • Contract all nodes in the group into a single node
  4. Build the new TreeTN with the fused structure
§Arguments
  • target - The target SiteIndexNetwork defining the desired structure
§Returns

A new TreeTN with the fused structure, or an error if:

  • The target structure is incompatible with the current structure
  • Nodes to be fused are not connected
§Properties
  • Bond dimension: Unchanged (pure contraction, no truncation)
  • Commutative: Non-overlapping groups can be merged in any order
§Example
Before: x1_1---x2_1---x1_2---x2_2---x1_3---x2_3  (6 nodes)
After:  {x1_1,x2_1}---{x1_2,x2_2}---{x1_3,x2_3}  (3 nodes)
Source

pub fn split_to<TargetV>( &self, target: &SiteIndexNetwork<TargetV, T::Index>, options: &SplitOptions, ) -> Result<TreeTN<T, TargetV>>
where TargetV: Clone + Hash + Eq + Ord + Send + Sync + Debug,

Split nodes to match the target structure.

This operation splits nodes that contain site indices belonging to multiple target nodes. The target structure must be a “refinement” of the current structure: each current node’s site indices should map to one or more target nodes.

§Algorithm (Two-Phase Approach)

Phase 1: Exact factorization (no truncation)

  1. Build mapping: site index ID -> target node name
  2. For each current node, check if its site indices map to multiple target nodes
  3. If so, split the node using QR factorization
  4. Repeat until all nodes match the target structure

Phase 2: Truncation sweep (optional) If options.final_sweep is true, perform a truncation sweep to optimize bond dimensions globally.

§Arguments
  • target - The target SiteIndexNetwork defining the desired structure
  • options - Options controlling truncation and final sweep
§Returns

A new TreeTN with the split structure, or an error if:

  • The target structure is incompatible with the current structure
  • Factorization fails
§Properties
  • Bond dimension: May increase during split, controlled by truncation
  • Exact (Phase 1): Without truncation, represents the same tensor
§Example
Before: {x1_1,x2_1}---{x1_2,x2_2}---{x1_3,x2_3}  (3 nodes, fused)
After:  x1_1---x2_1---x1_2---x2_2---x1_3---x2_3  (6 nodes, interleaved)
Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source

pub fn truncate( self, canonical_region: impl IntoIterator<Item = V>, options: TruncationOptions, ) -> Result<Self>
where V: Ord, <T::Index as IndexLike>::Id: Ord,

Truncate the network towards the specified center using options.

This is the recommended unified API for truncation. It accepts:

  • Center nodes specified by their node names (V)
  • TruncationOptions to control the SVD policy and max_rank
§Algorithm
  1. Canonicalize the network towards the center (required for truncation)
  2. Generate a two-site sweep plan using Euler tour traversal
  3. Apply SVD-based truncation at each step, visiting each edge twice
§Examples
use tensor4all_treetn::{TreeTN, TruncationOptions};
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};

// Build a 2-node chain
let s0 = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(3);
let s1 = DynIndex::new_dyn(2);

let t0 = TensorDynLen::from_dense(
    vec![s0.clone(), bond.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0, 0.0, 0.0],
).unwrap();
let t1 = TensorDynLen::from_dense(
    vec![bond.clone(), s1.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0, 0.0, 0.0],
).unwrap();

let tn = TreeTN::<_, String>::from_tensors(
    vec![t0, t1],
    vec!["A".to_string(), "B".to_string()],
).unwrap();

// Truncate with max rank 2 towards node "A"
let tn = tn.truncate(
    ["A".to_string()],
    TruncationOptions::default().with_max_rank(2),
).unwrap();

// Bond dimension is now at most 2
assert_eq!(tn.node_count(), 2);
Source

pub fn truncate_mut( &mut self, canonical_region: impl IntoIterator<Item = V>, options: TruncationOptions, ) -> Result<()>
where V: Ord, <T::Index as IndexLike>::Id: Ord,

Truncate the network in-place towards the specified center using options.

This is the &mut self version of Self::truncate.

Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source

pub fn new() -> Self

Create a new empty TreeTN.

Use add_tensor() to add tensors and connect() to establish bonds manually.

Source

pub fn from_tensors(tensors: Vec<T>, node_names: Vec<V>) -> Result<Self>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync, V: Ord,

Create a TreeTN from a list of tensors and node names using einsum rule.

This function connects tensors that share common indices (by ID). The algorithm is O(n) where n is the number of tensors:

  1. Add all tensors as nodes
  2. Build a map from index ID to (node, index) pairs in a single pass
  3. Connect nodes that share the same index ID
§Arguments
  • tensors - Vector of tensors to add to the network
  • node_names - Vector of node names corresponding to each tensor
§Returns

A new TreeTN with tensors connected by common indices, or an error if:

  • The lengths of tensors and node_names don’t match
  • An index ID appears in more than 2 tensors (TreeTN is a tree, so each bond connects exactly 2 nodes)
  • Connection fails (e.g., dimension mismatch)
§Errors

Returns an error if validation fails or connection fails.

§Examples
use tensor4all_treetn::TreeTN;
use tensor4all_core::{DynIndex, TensorDynLen, TensorLike};

let s0 = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(3);
let s1 = DynIndex::new_dyn(2);

let t0 = TensorDynLen::from_dense(
    vec![s0.clone(), bond.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0, 0.0, 0.0],
).unwrap();
let t1 = TensorDynLen::from_dense(
    vec![bond.clone(), s1.clone()],
    vec![1.0_f64, 0.0, 0.0, 1.0, 0.0, 0.0],
).unwrap();

let tn = TreeTN::<_, String>::from_tensors(
    vec![t0, t1],
    vec!["A".to_string(), "B".to_string()],
).unwrap();

assert_eq!(tn.node_count(), 2);
assert_eq!(tn.edge_count(), 1);
Source

pub fn add_tensor(&mut self, node_name: V, tensor: T) -> Result<NodeIndex>

Add a tensor to the network with a node name.

Returns the NodeIndex for the newly added tensor.

Also updates the site_index_network with the physical indices (all indices initially, as no connections exist yet).

Source

pub fn add_tensor_auto_name(&mut self, tensor: T) -> NodeIndex

Add a tensor to the network using NodeIndex as the node name.

This method only works when V = NodeIndex.

Returns the NodeIndex for the newly added tensor.

Source

pub fn connect( &mut self, node_a: NodeIndex, index_a: &T::Index, node_b: NodeIndex, index_b: &T::Index, ) -> Result<EdgeIndex>

Connect two tensors via a specified pair of indices.

The indices must have the same ID (Einsum mode).

§Arguments
  • node_a - First node
  • index_a - Index on first node to use for connection
  • node_b - Second node
  • index_b - Index on second node to use for connection
§Returns

The EdgeIndex of the new connection, or an error if validation fails.

Source§

impl<T, V> TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source

pub fn tensor(&self, node: NodeIndex) -> Option<&T>

Get a reference to a tensor by NodeIndex.

Source

pub fn tensor_mut(&mut self, node: NodeIndex) -> Option<&mut T>

Get a mutable reference to a tensor by NodeIndex.

Source

pub fn replace_tensor( &mut self, node: NodeIndex, new_tensor: T, ) -> Result<Option<T>>

Replace a tensor at the given node with a new tensor.

Validates that the new tensor contains all indices used in connections to this node. Returns an error if any connection index is missing.

Returns the old tensor if the node exists and validation passes.

Source

pub fn bond_index(&self, edge: EdgeIndex) -> Option<&T::Index>

Get the bond index for a given edge.

Source

pub fn bond_index_mut(&mut self, edge: EdgeIndex) -> Option<&mut T::Index>

Get a mutable reference to the bond index for a given edge.

Source

pub fn edges_for_node(&self, node: NodeIndex) -> Vec<(EdgeIndex, NodeIndex)>

Get all edges connected to a node.

Source

pub fn replace_edge_bond( &mut self, edge: EdgeIndex, new_bond_index: T::Index, ) -> Result<()>

Replace the bond index for an edge (e.g., after SVD creates a new bond index).

Also updates site_index_network: the old bond index becomes physical again, and the new bond index is removed from physical indices.

Source

pub fn sim_linkinds(&self) -> Result<Self>
where T::Index: IndexLike,

Return a copy with all link/bond indices replaced by fresh IDs.

This is analogous to ITensorMPS.jl’s sim(linkinds, M) / sim!(linkinds, M), and is mainly useful to avoid accidental index-ID collisions when combining multiple networks.

Notes:

  • This keeps dimensions and conjugate states, but changes identities.
  • This updates both endpoint tensors and internal bookkeeping.
Source

pub fn sim_linkinds_mut(&mut self) -> Result<()>
where T::Index: IndexLike,

Replace all link/bond indices with fresh IDs in-place.

See Self::sim_linkinds for details.

Source

pub fn set_ortho_towards(&mut self, index: &T::Index, dir: Option<V>)

Set the orthogonalization direction for an index (bond or site).

The direction is specified as a node name (or None to clear).

§Arguments
  • index - The index to set ortho direction for
  • dir - The node name that the ortho points towards, or None to clear
Source

pub fn ortho_towards_for_index(&self, index: &T::Index) -> Option<&V>

Get the node name that the orthogonalization points towards for an index.

Returns None if ortho_towards is not set for this index.

Source

pub fn set_edge_ortho_towards( &mut self, edge: EdgeIndex, dir: Option<V>, ) -> Result<()>

Set the orthogonalization direction for an edge (by EdgeIndex).

This is a convenience method that looks up the bond index and calls set_ortho_towards.

The direction is specified as a node name (or None to clear). The node must be one of the edge’s endpoints.

Source

pub fn ortho_towards_node(&self, edge: EdgeIndex) -> Option<&V>

Get the node name that the orthogonalization points towards for an edge.

Returns None if ortho_towards is not set for this edge’s bond index.

Source

pub fn ortho_towards_node_index(&self, edge: EdgeIndex) -> Option<NodeIndex>

Get the NodeIndex that the orthogonalization points towards for an edge.

Returns None if ortho_towards is not set for this edge’s bond index.

Source

pub fn validate_tree(&self) -> Result<()>

Validate that the graph is a tree (or forest).

Checks:

  • The graph is connected (all nodes reachable from the first node)
  • For each connected component: edges = nodes - 1 (tree condition)
Source

pub fn node_count(&self) -> usize

Get the number of nodes in the network.

Source

pub fn edge_count(&self) -> usize

Get the number of edges in the network.

Source

pub fn node_index(&self, node_name: &V) -> Option<NodeIndex>

Get the NodeIndex for a node by name.

Source

pub fn rename_node(&mut self, old_name: &V, new_name: V) -> Result<()>

Rename an existing node while preserving topology, site space, and orthogonality metadata.

Source

pub fn edge_between(&self, node_a: &V, node_b: &V) -> Option<EdgeIndex>

Get the EdgeIndex for the edge between two nodes by name.

Returns None if either node doesn’t exist or there’s no edge between them.

Source

pub fn node_indices(&self) -> Vec<NodeIndex>

Get all node indices in the tree tensor network.

Source

pub fn node_names(&self) -> Vec<V>

Get all node names in the tree tensor network.

Source

pub fn edges_to_canonicalize_by_names(&self, target: &V) -> Option<Vec<(V, V)>>

Compute edges to canonicalize from leaves to target, returning node names.

Returns (from, to) pairs in the order they should be processed:

  • from is the node being factorized
  • to is the parent node (towards target)

This is useful for contract_zipup and similar algorithms that work with node names rather than NodeIndex.

§Arguments
  • target - Target node name for the orthogonality center
§Returns

None if target node doesn’t exist, otherwise a vector of (from, to) pairs.

Source

pub fn canonical_region(&self) -> &HashSet<V>

Get a reference to the orthogonalization region (using node names).

When empty, the network is not canonicalized.

Source

pub fn is_canonicalized(&self) -> bool

Check if the network is canonicalized.

Returns true if canonical_region is non-empty, false otherwise.

Source

pub fn set_canonical_region( &mut self, region: impl IntoIterator<Item = V>, ) -> Result<()>

Set the orthogonalization region (using node names).

Validates that all specified nodes exist in the graph.

Source

pub fn clear_canonical_region(&mut self)

Clear the orthogonalization region (mark network as not canonicalized).

Also clears the canonical form.

Source

pub fn canonical_form(&self) -> Option<CanonicalForm>

Get the current canonical form.

Returns None if not canonicalized.

Source

pub fn add_to_canonical_region(&mut self, node_name: V) -> Result<()>

Add a node to the orthogonalization region.

Validates that the node exists in the graph.

Source

pub fn remove_from_canonical_region(&mut self, node_name: &V) -> bool

Remove a node from the orthogonalization region.

Returns true if the node was in the region, false otherwise.

Source

pub fn site_index_network(&self) -> &SiteIndexNetwork<V, T::Index>

Get a reference to the site index network.

The site index network contains both topology (graph structure) and site space (physical indices).

Source

pub fn site_index_network_mut(&mut self) -> &mut SiteIndexNetwork<V, T::Index>

Get a mutable reference to the site index network.

Source

pub fn site_space(&self, node_name: &V) -> Option<&HashSet<T::Index>>

Get a reference to the site space (physical indices) for a node.

Source

pub fn site_space_mut( &mut self, node_name: &V, ) -> Option<&mut HashSet<T::Index>>

Get a mutable reference to the site space (physical indices) for a node.

Source

pub fn share_equivalent_site_index_network(&self, other: &Self) -> bool
where <T::Index as IndexLike>::Id: Ord,

Check if two TreeTNs share equivalent site index network structure.

Two TreeTNs share equivalent structure if:

  • Same topology (nodes and edges)
  • Same site space for each node

This is used to verify that two TreeTNs can be added or contracted.

§Arguments
  • other - The other TreeTN to check against
§Returns

true if the networks share equivalent site index structure, false otherwise.

Source

pub fn same_topology(&self, other: &Self) -> bool

Check if two TreeTNs have the same topology (graph structure).

This only checks that both networks have the same nodes and edges, not that they have the same site indices.

Useful for operations like contract_zipup where we need networks with the same structure but possibly different site indices.

Source

pub fn same_appearance(&self, other: &Self) -> bool
where <T::Index as IndexLike>::Id: Ord, V: Ord,

Check if two TreeTNs have the same “appearance”.

Two TreeTNs have the same appearance if:

  1. They have the same topology (same nodes and edges)
  2. They have the same external indices (physical indices) at each node (compared as sets, so order within a node doesn’t matter)
  3. They have the same orthogonalization direction (ortho_towards) on each edge

This is a weaker check than share_equivalent_site_index_network:

  • share_equivalent_site_index_network: checks topology + site space (indices)
  • same_appearance: checks topology + site space + ortho_towards directions

Note: This does NOT compare tensor data, only structural information. Note: Bond index IDs may differ between the two TreeTNs (e.g., after independent canonicalization), so we compare ortho_towards by edge position, not by index ID.

§Arguments
  • other - The other TreeTN to compare against
§Returns

true if both TreeTNs have the same appearance, false otherwise.

Source

pub fn swap_site_indices( &mut self, target_assignment: &HashMap<<T::Index as IndexLike>::Id, V>, options: &SwapOptions, ) -> Result<()>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync, V: Ord,

Reorder site indices so that each index id ends up at the target node.

Builds a pre-computed schedule from the topology plus current and target site assignments, canonicalizes the network to the schedule root, then executes the scheduled transport and swap steps. Partial assignment is supported: indices not listed in target_assignment stay on their current side of every visited edge.

§Arguments
  • target_assignment - Map from site index id to target node name.
  • options - Truncation options for each SVD (default: no truncation, exact).
§Errors

Returns an error if target nodes are missing, an index id is unknown, or sweep fails.

Source

pub fn swap_site_indices_by_index( &mut self, target_assignment: &HashMap<T::Index, V>, options: &SwapOptions, ) -> Result<()>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync, T::Index: Hash + Eq, V: Ord,

Reorder site indices so that each index ends up at the target node.

Index-based version of swap_site_indices. Accepts T::Index keys instead of T::Index::Id.

§Examples
use std::collections::HashMap;

use tensor4all_core::{DynIndex, IndexLike, TensorDynLen};
use tensor4all_treetn::{SwapOptions, TreeTN};

let node_name_a = "A".to_string();
let node_name_b = "B".to_string();
let idx_a = DynIndex::new_dyn(2);
let idx_b = DynIndex::new_dyn(2);
let bond = DynIndex::new_dyn(1);
let t0 = TensorDynLen::from_dense(vec![idx_a.clone(), bond.clone()], vec![1.0, 0.0])?;
let t1 = TensorDynLen::from_dense(vec![bond, idx_b.clone()], vec![1.0, 0.0])?;
let mut treetn = TreeTN::<TensorDynLen, String>::from_tensors(
    vec![t0, t1],
    vec![node_name_a.clone(), node_name_b.clone()],
)?;

let mut target = HashMap::new();
target.insert(idx_a.clone(), node_name_b.clone());

treetn.swap_site_indices_by_index(&target, &SwapOptions::default())?;

assert_eq!(
    treetn
        .site_index_network()
        .find_node_by_index_id(idx_a.id())
        .map(|name| name.as_str()),
    Some(node_name_b.as_str())
);
assert!(treetn.is_canonicalized());
Source

pub fn verify_internal_consistency(&self) -> Result<()>
where <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync, V: Clone + Hash + Eq + Ord + Send + Sync + Debug,

Verify internal data consistency by checking structural invariants and reconstructing the TreeTN.

This function performs two categories of checks:

§Structural invariants (fail-fast checks):

0a. Connectivity: All tensors must form a single connected component 0b. Index sharing: Only edge-connected (adjacent) nodes may share index IDs. Non-adjacent nodes sharing an index ID violates tree structure assumptions.

§Reconstruction consistency:

After structural checks pass, clones all tensors and node names, reconstructs a new TreeTN using from_tensors, and verifies:

  1. Topology: Same nodes and edges
  2. Site space: Same physical indices for each node
  3. Tensors: Same tensor data at each node

This is useful for debugging and testing to ensure that the internal state of a TreeTN is consistent after complex operations.

§Returns

Ok(()) if the internal data is consistent, or Err with details about the inconsistency.

Trait Implementations§

Source§

impl<T, V> Clone for TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T, V> Debug for TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T, V> Default for TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Send + Sync + Debug,

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<T, V> Operator<T, V> for TreeTN<T, V>
where T: TensorLike, T::Index: IndexLike + Clone + Hash + Eq, V: Clone + Hash + Eq + Send + Sync + Debug,

Source§

fn site_indices(&self) -> HashSet<T::Index>

Get all site indices this operator acts on. Read more
Source§

fn site_index_network(&self) -> &SiteIndexNetwork<V, T::Index>

Get the site index network describing this operator’s structure. Read more
Source§

fn node_names(&self) -> HashSet<V>

Get the set of node names this operator covers. Read more
Source§

impl<T, V> TensorIndex for TreeTN<T, V>
where T: TensorLike, V: Clone + Hash + Eq + Ord + Send + Sync + Debug, <T::Index as IndexLike>::Id: Clone + Hash + Eq + Ord + Debug + Send + Sync,

Source§

fn external_indices(&self) -> Vec<Self::Index>

Return all external (site/physical) indices from all nodes.

This collects all site indices from site_index_network. Bond indices are NOT included (they are internal to the network).

Source§

fn replaceind( &self, old_index: &Self::Index, new_index: &Self::Index, ) -> Result<Self>

Replace an index in this TreeTN.

Looks up the index location (site or link) and replaces it in:

  • The tensor containing it
  • The appropriate index network (site_index_network or link_index_network)

Note: replace_tensor automatically updates the site_index_network based on the new tensor’s indices, so we don’t need to manually call replace_site_index.

Source§

fn replaceinds( &self, old_indices: &[Self::Index], new_indices: &[Self::Index], ) -> Result<Self>

Replace multiple indices in this TreeTN.

Source§

type Index = <T as TensorIndex>::Index

The index type used by this object.
Source§

fn num_external_indices(&self) -> usize

Number of external indices. Read more
§

fn replaceinds_pairs( &self, pairs: &[(Self::Index, Self::Index)], ) -> Result<Self, Error>

Replace indices using pairs of (old, new). Read more

Auto Trait Implementations§

§

impl<T, V> Freeze for TreeTN<T, V>

§

impl<T, V> RefUnwindSafe for TreeTN<T, V>
where <T as TensorIndex>::Index: RefUnwindSafe, V: RefUnwindSafe, <<T as TensorIndex>::Index as IndexLike>::Id: RefUnwindSafe, T: RefUnwindSafe,

§

impl<T, V> Send for TreeTN<T, V>

§

impl<T, V> Sync for TreeTN<T, V>

§

impl<T, V> Unpin for TreeTN<T, V>
where <T as TensorIndex>::Index: Unpin, V: Unpin, <<T as TensorIndex>::Index as IndexLike>::Id: Unpin, T: Unpin,

§

impl<T, V> UnsafeUnpin for TreeTN<T, V>

§

impl<T, V> UnwindSafe for TreeTN<T, V>
where <T as TensorIndex>::Index: UnwindSafe, V: UnwindSafe, <<T as TensorIndex>::Index as IndexLike>::Id: UnwindSafe, T: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<U> As for U

§

fn as_<T>(self) -> T
where T: CastFrom<U>,

Casts self to type T. The semantics of numeric casting with the as operator are followed, so <T as As>::as_::<U> can be used in the same way as T as U for numeric conversions. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
§

impl<T> ByRef<T> for T

§

fn by_ref(&self) -> &T

§

impl<T> ByRef<T> for T

§

fn by_ref(&self) -> &T

§

impl<T> ByRef<T> for T

§

fn by_ref(&self) -> &T

Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> DistributionExt for T
where T: ?Sized,

§

fn rand<T>(&self, rng: &mut (impl Rng + ?Sized)) -> T
where Self: Distribution<T>,

§

impl<T> DistributionExt for T
where T: ?Sized,

§

fn rand<T>(&self, rng: &mut (impl Rng + ?Sized)) -> T
where Self: Distribution<T>,

§

impl<T> DistributionExt for T
where T: ?Sized,

§

fn rand<T>(&self, rng: &mut (impl Rng + ?Sized)) -> T
where Self: Distribution<T>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
§

impl<T> Pointable for T

§

const ALIGN: usize

The alignment of pointer.
§

type Init = T

The type for initializers.
§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

§

fn vzip(self) -> V

§

impl<T, U> Imply<T> for U
where T: ?Sized, U: ?Sized,

§

impl<T> MaybeSend for T

§

impl<T> MaybeSendSync for T

§

impl<T> MaybeSync for T