Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Affine Transformation

An affine transformation evaluates a function at shifted or mixed coordinates. The tutorial uses the pullback point of view: to get the new value at an output point, look up the old function at the transformed input point.

Runnable source: docs/tutorial-code/src/bin/qtt_affine.rs

Key API Pieces

AffineParams stores the matrix and offset. Boundary conditions say what happens when transformed coordinates leave the grid. The operator is applied via tensor_train_to_treetn, align_to_state, and apply_linear_operator. The source function is f(u, v) = sin(2πu/N) + 0.5 cos(2πv/N) + 0.25 sin(2π(u + 2v)/N).

fn main() -> anyhow::Result<()> {
use tensor4all_core::TensorIndex;
use tensor4all_quanticstci::{
    quanticscrossinterpolate_discrete, QtciOptions, UnfoldingScheme,
};
use tensor4all_quanticstransform::{
    affine_operator, AffineParams, BoundaryCondition,
};
use tensor4all_treetn::{apply_linear_operator, tensor_train_to_treetn, ApplyOptions};
use std::f64::consts::PI;

let bits = 7;
let n = 1usize << bits;
let source_grid = [n, n];
let source_function = move |grid_1based: &[i64]| -> f64 {
    let u = (grid_1based[0] - 1) as f64;
    let v = (grid_1based[1] - 1) as f64;
    let n = n as f64;
    (2.0 * PI * u / n).sin()
        + 0.5 * (2.0 * PI * v / n).cos()
        + 0.25 * (2.0 * PI * (u + 2.0 * v) / n).sin()
};
let source_options = QtciOptions::default()
    .with_unfoldingscheme(UnfoldingScheme::Fused)
    .with_verbosity(0);

let (source, _, _) = quanticscrossinterpolate_discrete::<f64, _>(
    &source_grid,
    source_function,
    None,
    source_options,
)?;

let params = AffineParams::from_integers(vec![1, 1, 0, 1], vec![0, 0], 2, 2)?;
let mut operator = affine_operator(bits, &params, &[BoundaryCondition::Periodic; 2])?
    .transpose();

let source_tt = source.tensor_train();
let (state, _indices) = tensor_train_to_treetn(&source_tt)?;

operator.align_to_state(&state)?;
let result = apply_linear_operator(&operator, &state, ApplyOptions::naive())?;

let external = TensorIndex::external_indices(&result);
assert_eq!(external.len(), bits);
assert!(result.node_count() >= state.node_count());
Ok(())
}

The full tutorial repeats the same workflow for two boundary conditions and compares periodic and open results.

What It Computes

The example builds a two-dimensional QTT, creates an affine operator, applies it to the QTT, and compares the transformed values with a direct reference.

Affine transformed values

Affine transformation error

The operator has its own bond dimensions, and the transformed QTT has another set. Both are useful when judging the cost of the operation.

Bond dimensions after the affine transformation

Bond dimensions of the affine operator