Experimental tree RSI products
tensor4all-treersi approximates simultaneous Hadamard products of existing
tree tensor networks. All inputs must have the same labeled tree
and matching physical indices at each node. Input bond dimensions may differ.
The implementation supports four real/complex scalar types and CPU execution.
Use hadamard_many_in with the same execution context that owns the inputs.
The convenience hadamard_many uses the default CPU context and requires the
global-defaults feature. Set max_bond_dim or sketch_dim; start with a rank
budget appropriate to the application and validate actual product error.
rel_tol controls local sketch pivots and is not a global error tolerance.
The example below takes a nonconstant bond-two chain, uses sketched columns, and checks the full squared tensor against a dense oracle. Dense validation is appropriate for this small example. Materialize each tensor once.
use tensor4all_core::{DynIndex, IdxTensor};
use tensor4all_treersi::{hadamard_many, TreeRsiOptions};
use tensor4all_treetn::TreeTN;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let sites: Vec<_> = (0..7).map(|_| DynIndex::new_dyn(2)).collect();
let bonds: Vec<_> = (0..6).map(|_| DynIndex::new_dyn(2)).collect();
let mut cores = Vec::new();
for v in 0..7 {
let mut indices = vec![sites[v].clone()];
if v > 0 {
indices.push(bonds[v - 1].clone());
}
if v < 6 {
indices.push(bonds[v].clone());
}
// Column-major core entries; nonconstant bond-two input.
let data = (0..(1 << indices.len()))
.map(|j| 0.3 + ((j * 13 + v * 7) % 17) as f64 / 20.0)
.collect();
cores.push(IdxTensor::from_dense(indices, data)?);
}
let input = TreeTN::from_tensors(cores, (0usize..7).collect())?;
let dense = input.to_dense()?.permute_indices(&sites)?.to_vec::<f64>()?;
let expected = IdxTensor::from_dense(sites, dense.iter().map(|x| x * x).collect())?;
let options = TreeRsiOptions {
max_bond_dim: Some(4),
sketch_dim: Some(3),
seed: 7,
..Default::default()
};
let result = hadamard_many::<f64, _>(&[input.clone(), input], &options)?;
assert!(result
.diagnostics
.edges
.iter()
.any(|edge| !edge.exact_columns));
let error = result.tree.to_dense()?.sub(&expected)?.maxabs()? / expected.maxabs()?;
assert!(error < 1e-10, "actual product relative max error: {error}");
Ok(())
}
For larger problems, choose held-out points independently of the sketch RNG,
compute each input there using TreeTNCachedEvaluator::evaluate_batched, and
compare their pointwise product to the returned tensor at those same points.
Keep the reference values outside any timing loop. A sampled relative L2
error is useful evidence, but does not bound error over all physical entries.
Use a fixed acceptance threshold; reject failed calls and nonfinite values.
The edge diagnostics describe local row selection. Small relative_pivot,
a low output rank, or successful return does not certify accuracy. The method
can miss even a rank-one true product for legal explicit probes. See the
derivation and counterexample.
Binary scales accompany contractions to handle long chains and extreme overall
magnitudes. Detected loss of representable numerical range is an error.
max_local_elements limits individual dense blocks; total cache and backend
scratch memory are additional. Only products are supported: there is no
arbitrary nonlinear map or reciprocal API and no automatic differentiation.