Skip to content

trunc_svd ignores target_trunc_err when it is exactly 0 #22

Description

@ultimatile

tci::trunc_svd applies target_trunc_err only when it is strictly positive, so a caller passing exactly 0 gets no error-based truncation at all. The TCAPI specification (0.x dev) gives the criterion as epsilon(chi) <= target_trunc_err, where epsilon(chi) is the discarded weight sum_{i>=chi} s_i^2 divided by the total sum_i s_i^2, indices 0-based, and chi is the retained bond dimension. The inequality is inclusive, so a target of 0 still admits every chi whose discarded weight is exactly zero.

This backend declares two trunc_svd overloads in include/tci/cytnx_typed_tensor_impl.h, distinguished by their truncation-control parameters: (chi_min, chi_max, target_trunc_err, s_min), called the general form below, and (chi_max, s_min). s_min is the value below which singular values are dropped, so an argument of 0 drops none.

Evidence

In the general form the selection is gated on a strict inequality:

if (target_trunc_err > 0.0 && bond_dim > chi_min) {
  // walk chi and compare epsilon(chi) against target_trunc_err
}

With target_trunc_err = 0 the block never runs, so the retained bond dimension stays at whatever the SVD returned under chi_max and s_min, and is afterwards only raised to chi_min, never lowered. Given a rank-deficient input, an s_min of 0, and a chi_max at or above the number of singular values the SVD computes, the exactly-zero singular values are therefore all retained — although discarding them costs epsilon = 0, which the criterion admits.

The reported trunc_err is computed after that block and is written on every path, so it is not affected.

Why this is not a one-character change

Relaxing the guard to >= 0.0 also changes the (chi_max, s_min) overload, because it delegates to the general form with a zero target:

// Call full version with chi_min=1, target_trunc_err=0
constexpr bond_dim_t<TenT> chi_min = 1;
constexpr real_t<TenT> target_trunc_err = 0.0;
trunc_svd(ctx, a, num_of_bds_as_row, u, s_diag, v_dag, trunc_err, chi_min, chi_max,
          target_trunc_err, s_min);

Callers of that overload would then start dropping exactly-zero singular values, and its parameter list gives them no way to opt out — there is no target to raise.

So the guard cannot be decided on its own. Settling it means settling what the (chi_max, s_min) overload promises about rank-deficient input, and in particular whether the specification intends that overload to be exactly the general form at chi_min = 1, target_trunc_err = 0 — which is what the delegation above assumes. No question has been raised on the specification side yet.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions