CTM options#

Every option accepted by the CTM routines is a field of one of three dataclasses. They are the single place each default is defined, and they are what the loose keyword arguments below are normalized into.

Object

Covers

yastn.tn.fpeps.envs.CTMOpts

one CTM sweep: moves, projector method, truncation, convergence, execution

yastn.tn.fpeps.envs.SIOpts

the subspace-iteration projector mode, nested under CTMOpts.opts_si

yastn.tn.fpeps.envs.FixedPointOpts

the forward / fixed-point pair plus the Neumann backward controls

Two ways to pass them#

Loose keyword arguments remain the common case:

env.ctmrg_(opts_svd={'D_total': chi}, max_sweeps=200, corner_tol=1e-8)

Alternatively, build the options once and reuse them. This is what the opts= keyword is for:

from yastn.tn.fpeps.envs import make_ctm_opts

opts = make_ctm_opts(opts_svd={'D_total': chi}, max_sweeps=200, corner_tol=1e-8)
env_a.ctmrg_(opts=opts)
env_b.ctmrg_(opts=opts, max_sweeps=10)      # same, but ten sweeps

Explicit keywords win over opts, which in turn wins over env.default_opts if that is set. Anything left unspecified falls back to the dataclass default. Deriving never mutates the original: options are frozen, and nested bundles are merged into the base rather than replacing it, so make_ctm_opts(opts, opts_svd={'policy': 'fullrank'}) keeps the rest of opts.opts_svd.

Unknown options are rejected#

A name that is not an option raises YastnError listing the accepted fields:

>>> env.ctmrg_(opts_svd={'D_total': 8}, max_sweep=10)
YastnError: CTM option 'max_sweep' not recognized. Accepted: checkpoint_move, conv_check, ...

Some older spellings are still accepted and normalized: opts_svd_ctm for opts_svd, iterator for iterator_step, asvr_iterations for adaptive_spectrum_iterations, and the refinement acronyms 'cwo', 'asvr', 'rds' for their spelled-out names.

From a configuration file, with command-line overrides#

The options are plain stdlib dataclasses holding config-representable types, so they round-trip through nested dictionaries. No configuration framework is required or assumed:

import yaml
from yastn.tn.fpeps.envs import CTMOpts, from_dict, override, to_dict

opts = from_dict(CTMOpts, yaml.safe_load(open('ctm.yaml')))     # defaults <- file
opts = override(opts, {'opts_svd.D_total': 64,                  #          <- CLI
                       'max_sweeps': 200,
                       'opts_si.niter': 3})
env.ctmrg_(opts=opts)

override() addresses fields by dotted path, including keys inside opts_svd and fields of the nested SIOpts. Each layer is validated the same way, so an unknown name in the file or on the command line is an error. Unlike the factories – where None means “not supplied”, so a function signature can forward its defaults harmlessly – an override of None is an explicit assignment and does clear the field.

argspec() yields (dotted_name, type, default, help, choices) for every field, so a command-line interface can be generated from the schema rather than restating it:

for name, typ, default, help_, choices in argspec(CTMOpts):
    parser.add_argument(f'--{name}', default=default, help=help_,
                        **({'choices': choices} if choices else {}))

One limit is worth knowing: a callable has no representation in a configuration file. conv_check and a mask_f inside opts_svd round-trip in memory but not through YAML or JSON, so they must be set from code. to_dict() emits them as objects.

Reference#

class yastn.tn.fpeps.envs.CTMOpts(*, moves: str = 'hv', method: str = '2x2 corner', max_sweeps: int = 1, corner_tol: float | None = None, conv_check: Callable | None = None, iterator_step: int = 0, opts_svd: dict[str, ~typing.Any]=<factory>, use_qr: bool = True, cutoff: float = 0.0, opts_si: SIOpts | None = None, checkpoint_move: Literal[False, 'reentrant', 'nonreentrant']=False, devices: tuple[str, ...] | None=None, profiling_mode: str | None = None, verbosity: int = 0)[source]#

Options of a CTM environment sweep.

Construct with make_ctm_opts(), which accepts the legacy keyword spellings and reports unknown names. Direct construction is fine too; all normalization and validation happens in __post_init__, so dataclasses.replace and from_dict() are equally safe.

Parameters:
  • moves (str) – Sequence of moves forming a single sweep, drawn from 'l', 'r', 't', 'b', 'h', 'v' (and 'd' for the c4v variant). Sensible choices are 'hv' and 'lrtb'.

  • method (str) – Projector construction. Must contain '2x2', '1x2' or '2x1', or be exactly '1site' / '2site'.

  • max_sweeps (int) – Maximal number of sweeps.

  • corner_tol (float | None) – Convergence tolerance on the change of corner singular values. None disables the built-in check.

  • conv_check (Callable | None) – Custom convergence check f(env, history) -> (converged, history), used instead of the corner_tol comparison. Previously this was passed as a callable corner_tol; splitting the two keeps corner_tol expressible in a configuration file.

  • iterator_step (int) – Yield an intermediate result every that many sweeps. 0 runs all sweeps eagerly and returns a single result.

  • opts_svd (dict) – Options forwarded to yastn.linalg.svd_with_truncation(), setting the environment bond dimension. Copied on construction; treat the stored dict as read-only and go through svd_kwargs().

  • use_qr (bool) – Whether to include an intermediate QR while calculating projectors.

  • cutoff (float) – Absolute pseudo-inverse cutoff in projector construction.

  • opts_si (SIOpts | None) – Subspace-iteration options; see SIOpts. A plain dict is accepted and converted.

  • checkpoint_move (False | str) – 'reentrant' or 'nonreentrant' to checkpoint each move on the PyTorch backend; False disables checkpointing.

  • devices (tuple[str, …] | None) – Devices for the distributed CTM paths. A list is accepted and converted.

  • profiling_mode (str | None) – Profiling instrumentation, e.g. 'NVTX'.

  • verbosity (int) – Diagnostic verbosity.

property si_enabled: bool#

Whether SI projectors are active for this sweep.

si_trunc_kwargs() → dict[source]#

The subset of opts_svd the SI path forwards to truncation_mask.

Narrower than svd_kwargs() by design; see SI_TRUNCATION_KEYS.

svd_kwargs(**overrides) → dict[source]#

The keyword arguments for yastn.linalg.svd_with_truncation().

This is the single boundary at which options become SVD arguments. Returns a fresh dict every call, so a per-pair prediction such as svd_kwargs(k_block=...) never leaks into the next projector or the next sweep.

class yastn.tn.fpeps.envs.SIOpts(*, enabled: bool = False, oversampling: int = 5, niter: int = 5, tol: float = 0.001, warmup: int = 5, redistribute_sectors: bool = False, redistribute_frequency: int = 0, refinement: Literal['per_sector_oversampling', 'adaptive_spectrum', 'sector_dimensions'] = 'per_sector_oversampling', adaptive_spectrum_iterations: int = 5, skip_SI_update: bool = False, rebase: bool = True, recycle_grad: bool = False)[source]#

Options of the recycled subspace-iteration (SI) projectors.

Parameters:
  • enabled (bool) – Whether SI projectors are used at all. The default is False.

  • oversampling (int) – Extra directions carried beyond the requested bond dimension.

  • niter (int) – Number of subspace iterations when adjusting the range-finders.

  • tol (float) – Target subspace error of the range-finders, weighted by the singular value of each direction.

  • warmup (int) – Number of projector updates before the redistribution schedule starts.

  • redistribute_sectors (bool) – Reallocate the SI rank between charge sectors, on the warmup / redistribute_frequency schedule.

  • redistribute_frequency (int) – Redistribute every that many updates once past warmup. 0 disables the recurring redistribution.

  • refinement (str) – Sector-refinement algorithm: 'per_sector_oversampling', 'adaptive_spectrum' or 'sector_dimensions'.

  • adaptive_spectrum_iterations (int) – Refinement passes used by refinement='adaptive_spectrum'. Ignored by the other refinements.

  • skip_SI_update (bool) – Skip the subspace iteration entirely on an update that is past warmup, outside the redistribution schedule, and whose bases already report an error below tol. Trades accuracy for speed.

  • rebase (bool) – When changed corner legs invalidate the recycled bases, carry them onto the new row space instead of drawing fresh ones.

  • recycle_grad (bool) – Keep the recycled bases attached to the autograd graph.

class yastn.tn.fpeps.envs.FixedPointOpts(*, fwd: CTMOpts = <factory>, fp: CTMOpts = <factory>, neumann_patience: int = 10, devices: tuple[str, ...] | None=None, verbosity: int = 0)[source]#

Options of the fixed-point CTM, see yastn.tn.fpeps.envs.fixed_pt.fp_ctmrg().

FixedPoint reverses the CTM, so the forward settings carry over to the backward path by design: fp is fwd with selective overrides, and the Neumann series in the backward pass takes its iteration budget from fp.max_sweeps and its gradient tolerance from fp.corner_tol. The additional configuration specific to fixed point approach is defined here.

Parameters:
  • fwd (CTMOpts) – Options of the forward CTMRG convergence.

  • fp (CTMOpts) – Options of the single gauge-fixing CTM step, and – through max_sweeps and corner_tol – of the Neumann backward loop. Built from fwd with overrides applied.

  • neumann_patience (int) – Iterations without improvement before the Neumann series gives up. Unlike the budget and tolerance, this has no forward counterpart.

  • devices (tuple[str, …] | None) – Devices for the distributed fixed-point path. Was fp_devices.

  • verbosity (int) – Diagnostic verbosity.

classmethod from_legacy_dicts(ctm_opts_fwd=None, ctm_opts_fp=None, devices=None)[source]#

Build from the ctm_opts_fwd / ctm_opts_fp dicts of fp_ctmrg.

Reproduces the historical derivation exactly: fp starts as a copy of fwd and is then overridden selectively, with opts_svd merged key by key rather than replaced.

Keys that are not CTM options (neumann_patience, and the legacy fp_devices) are lifted out before the rest is handed to make_ctm_opts(), which would otherwise reject them.

property neumann_max_iter: int#

Neumann iteration budget, carried over from the FP step’s sweep budget.

property neumann_tol: float#

Neumann gradient tolerance, carried over from the FP step’s corner tolerance.

yastn.tn.fpeps.envs.make_ctm_opts(base: CTMOpts | None = None, **kwargs) → CTMOpts[source]#

Build CTMOpts from loose keyword arguments.

This is the single entry point through which every calling convention funnels – a direct keyword call, from_dict() for a configuration file, and override() for command-line layering – so the unknown-key error cannot be bypassed.

base supplies the starting values; anything passed as a keyword overrides it. iterator=True is accepted as the historical spelling of iterator_step=1, and opts_svd_ctm as that of opts_svd.

Parameters:

base (CTMOpts | None) – Options to derive from. None starts from the defaults.

Example

opts = make_ctm_opts(opts_svd={'D_total': 64}, max_sweeps=200,
                     corner_tol=1e-8, use_qr=False)
tighter = make_ctm_opts(opts, corner_tol=1e-10)
yastn.tn.fpeps.envs.make_si_opts(base: SIOpts | None = None, **kwargs) → SIOpts[source]#

Build SIOpts, accepting the legacy key spellings.

Unknown keys raise YastnError rather than being silently dropped.

yastn.tn.fpeps.envs.make_fixed_point_opts(base: FixedPointOpts | None = None, **kwargs) → FixedPointOpts[source]#

Build FixedPointOpts. fwd and fp accept plain dicts.

yastn.tn.fpeps.envs.to_dict(opts) → dict[source]#

Nested plain-dict representation, ready for YAML/JSON.

Note that a callable option has no serializable form: conv_check, and mask_f inside opts_svd, come out as the objects themselves. They round-trip in memory but not through a file, and from_dict() cannot set them – code must.

yastn.tn.fpeps.envs.from_dict(cls, d: dict)[source]#

Build an options object of type cls from a nested plain dict.

The inverse of to_dict(), and the entry point for a configuration file. Validates through the same factory as every other path, so an unknown key in the file is an error rather than a silent no-op.

yastn.tn.fpeps.envs.override(opts, overrides: dict)[source]#

Layer dotted-path overrides on top of opts, returning a new object.

The command-line half of the configuration story: a config file builds the base with from_dict(), then flags layered on top override individual leaves.

opts = override(opts, {'max_sweeps': 200, 'opts_svd.D_total': 64,
                       'opts_si.niter': 3})

A path may address a dataclass field, a nested dataclass field, or a key inside opts_svd. Each layer is validated.

yastn.tn.fpeps.envs.argspec(cls, prefix: str = '')[source]#

Yield (dotted_name, type, default, help, choices) for every option.

Lets a downstream application generate its command-line flags from the schema, so adding a CTM option never means editing that CLI. choices is derived from the Literal annotations, and help from each field’s metadata['help'].

for name, typ, default, help_, choices in argspec(CTMOpts):
    parser.add_argument(f'--{name}', default=default, help=help_,
                        **({'choices': choices} if choices else {}))

See also

Environment CTM for the CTMRG iteration and its SI mode, and Fixed-point CTMRG with implicit differentiation for the differentiable fixed point.