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.
tci::trunc_svdappliestarget_trunc_erronly 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 asepsilon(chi) <= target_trunc_err, whereepsilon(chi)is the discarded weightsum_{i>=chi} s_i^2divided by the totalsum_i s_i^2, indices 0-based, andchiis the retained bond dimension. The inequality is inclusive, so a target of 0 still admits everychiwhose discarded weight is exactly zero.This backend declares two
trunc_svdoverloads ininclude/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_minis 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:
With
target_trunc_err = 0the block never runs, so the retained bond dimension stays at whatever the SVD returned underchi_maxands_min, and is afterwards only raised tochi_min, never lowered. Given a rank-deficient input, ans_minof 0, and achi_maxat or above the number of singular values the SVD computes, the exactly-zero singular values are therefore all retained — although discarding them costsepsilon = 0, which the criterion admits.The reported
trunc_erris 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.0also changes the(chi_max, s_min)overload, because it delegates to the general form with a zero target: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 atchi_min = 1, target_trunc_err = 0— which is what the delegation above assumes. No question has been raised on the specification side yet.